thinkphp 5.1 跨域必须用全局中间件处理,因控制器内header()易被覆盖;需在corsmiddleware的handle()开头拦截options并返回204,再对非options请求动态设置access-control-allow-origin(不可为*)、credentials、headers等头。

ThinkPHP 5.1 没有内置跨域中间件,直接在控制器里写 header() 或用 response()->header() 大概率失效——因为框架后续可能覆盖响应头,或根本没走到你写的那行代码。
为什么加了 Access-Control-Allow-Origin 还报 CORS 错误
常见现象是浏览器控制台只显示 “No 'Access-Control-Allow-Origin' header is present”,但你在控制器里明明写了 header()。原因有三个:
- 输出已开始(比如前面有空格、BOM、
echo或日志),导致headers already sent报错,header()被忽略 - 请求是 OPTIONS 预检,但你的代码只对 GET/POST 生效,没拦截并响应 OPTIONS,浏览器直接卡死
- 用了
credentials: true(比如带 Cookie 或 token),却设了Access-Control-Allow-Origin: *,浏览器强制拒绝
CorsMiddleware 必须在 handle() 开头处理 OPTIONS
这是 TP5.1 唯一稳的方案:中间件必须抢在框架路由和控制器执行前介入,且对 OPTIONS 请求立即返回,不走后续逻辑。
- 用命令生成:
php think make:middleware CorsMiddleware - 编辑
app/middleware/CorsMiddleware.php,handle()方法开头就判断:if ($request->isOptions()) { return response('', 204); } - 所有
header()或$response->header()必须在这行return之前调用,否则 OPTIONS 响应不带 CORS 头 - 不要在
$next($request)之后设 header——此时响应体已生成,header 无效
域名白名单不能硬编码 *,尤其带凭证时
如果你前端用了 fetch(..., { credentials: 'include' }) 或 axios 的 withCredentials: true,Access-Control-Allow-Origin 就不能是 *,必须动态匹配并校验 origin。
- 读取来源:
$origin = $request->header('origin'); - 白名单数组(生产环境务必替换):
$allowed = ['https://admin.example.com', 'http://localhost:3000']; - 校验后设置:
if (in_array($origin, $allowed)) { $response->header('Access-Control-Allow-Origin', $origin); } - 同时必须设:
$response->header('Access-Control-Allow-Credentials', 'true'); - 漏掉
Access-Control-Allow-Headers会导致自定义头(如token、X-CSRF-TOKEN)被拒,要显式列出:Content-Type, Authorization, X-Requested-With, token
入口文件 public/index.php 是最后兜底位置
当所有中间件、路由、控制器都不可控(比如用了行为 behavior、或者某些插件绕过中间件),直接在 public/index.php 最顶部加 header() 是最暴力有效的兜底方式。
- 位置必须在
require __DIR__ . '/../thinkphp/start.php';之前 - 示例:
header('Access-Control-Allow-Origin: http://localhost:3000'); - 注意:这里无法做 origin 白名单校验,也不方便处理 OPTIONS,仅适合开发调试或极简场景
- 若需支持凭证,此处也必须配
Access-Control-Allow-Credentials: true,且 origin 不能为*
真正容易被忽略的是:TP5.1 的中间件执行顺序没有自动“包裹”整个请求生命周期,$next($request) 返回的 $response 对象可能已被框架内部修改,所以 header 必须在 return $next() 之前写入,而不是依赖返回后的 response 对象再 setHeader。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











