corsmiddleware必须置于app/middleware.php全局中间件数组首位,以确保在session、jwt等中间件前拦截options预检请求并返回204;需动态校验origin白名单、显式声明allow-headers字段、且credentials启用时origin不可为*。

中间件必须放在 app/middleware.php 数组最前面
ThinkPHP 的中间件是链式执行的,CorsMiddleware 必须排在第一位,否则像 SessionMiddleware、JWT 验证或日志中间件可能提前输出响应,导致 headers already sent 错误。常见错误是把它插在中间或末尾,结果 OPTIONS 请求根本没被拦截,浏览器卡死或返回 405。
- 正确写法:
return [\app\middleware\CorsMiddleware::class, \think\middleware\SessionInit::class, ...]; - 绝对不能写成字符串
'cors'或别名,必须用完整命名空间类名 - 多应用模式下,要改对应应用目录下的
app/middleware.php,不是根目录那个
handle() 里必须先拦截 OPTIONS 并返回 204
浏览器发预检请求时,只看状态码和响应头,不关心 body。不拦截就走正常流程,框架找不到匹配路由会报 404 或 405,真实请求永远发不出去。
- 必须在
$next($request)之前判断:if ($request->isOptions()) { return response('', 204)->header(...); } - 不能只设
Access-Control-Allow-Origin: *就完事——如果前端带credentials: 'include',这行直接让浏览器拒绝响应 - 推荐动态读取并白名单校验:
$origin = $request->header('origin');然后用in_array($origin, $allowedOrigins)判断是否放行
Access-Control-Allow-Headers 不能写 *(尤其带自定义 Header 时)
当请求含 Authorization、X-Auth-Token 或 Content-Type: application/json 时,浏览器强制预检,且要求 Access-Control-Allow-Headers 显式列出所有实际发送的字段,大小写也必须完全一致。
- 错例:
'Access-Control-Allow-Headers' => '*'—— 在大多数 ThinkPHP 版本中不被支持,Nginx/Chrome 会无视 - 对例:
['Content-Type', 'Authorization', 'X-Requested-With', 'X-Auth-Token'],每个字段名与前端实际发的一致 - 漏一个字段(比如前端发了
X-Trace-ID但没列进 allowHeaders),预检就失败,控制台报 “Request header field x-trace-id is not allowed by Access-Control-Allow-Headers”
带 Cookie 或 Token 时,Origin 和 Credentials 必须配对生效
这是 CORS 协议硬性限制,不是 ThinkPHP 能绕开的。只要前端 fetch 设置了 credentials: 'include',后端就不能回 Access-Control-Allow-Origin: *,否则浏览器静默丢弃响应。
- 必须动态取
$request->header('origin'),再比对白名单数组,匹配成功才设Access-Control-Allow-Origin: $origin - 必须同时设
Access-Control-Allow-Credentials: true,缺一不可 - 注意:一旦启用
credentials,Access-Control-Allow-Origin就不能再是通配符,也不能是空字符串或 null
Authorization,但真实接口响应没带这个头,有些旧版 Chrome 仍会拦截。所以最好在中间件里统一设置,而不是靠路由级 allowCrossDomain() 单独补。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











