ThinkPHP 6.0 默认不处理跨域,Access-Control-Allow-Origin 等头不会自动出现;路由级 ->allowCrossDomain() 必须链式调用在显式 HTTP 路由后,传参支持域名数组和 credentials 开关,且仅对该路由生效;全局中间件需手动拦截 OPTIONS 请求并返回 204,同时严格匹配 Origin 与 credentials 配置,否则跨域失败。

ThinkPHP 6.0 默认不处理跨域,Access-Control-Allow-Origin 这类头不会自动出现,哪怕你写了 ->allowCrossDomain() 也得看它有没有真正执行 —— 很多时候请求压根没走到那条路由,或者中间件顺序错位导致头被覆盖。
路由级 allowCrossDomain() 怎么写才生效
这个方法只对显式定义的 HTTP 路由有效,且必须链式调用、不能漏掉;它不是“开关”,而是给该路由绑定一个轻量 CORS 中间件。
- 必须写在
route/app.php的路由定义之后,例如:Route::post('api/login', 'LoginController@login')->allowCrossDomain(); - 传参时若要支持 Cookie,第二个参数必须为
true:->allowCrossDomain(['https://admin.example.com'], true),否则withCredentials: true前端会静默失败 - 不传域名数组时默认是
*,但一旦启用 credentials 就立刻被浏览器拒绝,所以开发环境用*可以,生产环境必须指定完整协议+域名+端口 - 闭包路由里调用容易遗漏括号或分号,比如写成
->allowCrossDomain(少())就完全不生效
全局中间件处理 OPTIONS 预检请求的硬要求
浏览器发非简单请求(如带 Authorization 或 Content-Type: application/json)前,必先发一次 OPTIONS。如果后端不明确响应它,就会卡在预检阶段,控制台报 405 Method Not Allowed 或直接无反应。
- 自定义中间件(如
app/middleware/CorsMiddleware.php)中,开头必须判断:if ($request->isOptions()) { return response()->code(204); } -
204是标准做法,返回空体 + 正确头;别用200加空 JSON,某些 Swoole 环境会因 Content-Length 不匹配出错 - 预检响应必须包含
Access-Control-Allow-Methods和Access-Control-Allow-Headers,尤其后者要覆盖前端实际发的头,比如Authorization或token - 别在同一个中间件里既设头又放行请求体 ——
$next($request)后再调$response->header()才安全;直接header()函数调用大概率被 TP6 的 PSR-7 响应流程覆盖
think\middleware\AllowCrossDomain 和 think\middleware\Cors 到底能不能用
这两个类名都存在,但行为和适用版本差异极大,混用极易翻车。
立即学习“PHP免费学习笔记(深入)”;
-
think\middleware\AllowCrossDomain是 TP6.0.x 内置类,但它只设Access-Control-Allow-Origin和基础头,**完全不处理 OPTIONS 请求**,也不读取config/cors.php—— 那个文件是 Laravel 的,TP6 不识别 -
think\middleware\Cors是 TP6.1+ 新增的官方中间件,支持配置驱动,但 6.0.x 升级不兼容,强行引入会报Class not found - 如果你用的是 TP6.0.13 这类稳定版,最稳方案是删掉所有内置 CORS 类引用,手写中间件,自己控制每个头的值和条件逻辑
- 注册位置必须在
app/middleware.php数组靠前(建议第 1 或 2 位),否则可能被后续中间件(如日志、权限)覆盖响应头
带 Cookie 的跨域为什么总失败
不是加了 Access-Control-Allow-Credentials: true 就完事,前后端三处必须同时对齐:
- 后端响应头:
Access-Control-Allow-Origin必须是具体域名(如https://a.com),不能是*;Access-Control-Allow-Credentials必须为true - 前端请求:fetch 要加
credentials: 'include',jQuery 要设xhrFields: { withCredentials: true } - 后端 Session 配置:
config/session.php中的'domain'得设为'.example.com'(注意开头的点),且'secure'要匹配当前协议(HTTPS 环境下必须为true) - 反向代理场景(Nginx → Swoole):Nginx 必须透传
Origin头,否则 PHP 收不到原始来源,动态匹配失效 —— 加一句proxy_set_header Origin $http_origin;
真正卡住的地方,从来不是“怎么加头”,而是谁在删头、谁在截断 OPTIONS、谁把 Origin 给吞了。检查顺序建议:先 curl 看原始响应头是否齐全,再查 Nginx access log 是否记录了 OPTIONS 请求,最后确认中间件 handle() 是否真被执行。



















