ThinkPHP跨域必须主动拦截OPTIONS预检并返回204,用$response->header()动态设头且中间件置顶;路由级allowCrossDomain()需链式调用末尾并传白名单+true启用凭证,否则带credentials时静默失败。

直接设 header() 基本无效,ThinkPHP 的响应生命周期会覆盖它;必须用框架原生方式注入响应头,且 OPTIONS 预检请求必须被主动拦截返回 204,否则浏览器根本不会发真实请求。
路由级 allowCrossDomain() 怎么写才生效
这是最轻量、最安全的方案,只对目标路由起作用,不污染其他接口。
- 必须链式调用在
Route::get()或Route::group()末尾,例如:Route::post('api/login', 'LoginController@login')->allowCrossDomain(['https://admin.example.com', 'http://localhost:5173'], true) - 第二个参数为
true时,Access-Control-Allow-Origin不再是*,而是动态取请求头中的Origin并精确匹配白名单 —— 匹配失败就无头,浏览器拒绝响应 - 该方法不处理
OPTIONS请求,所以若前端带credentials: 'include',而你又没配白名单或漏传true,就会静默失败 - 仅对 HTTP/HTTPS 路由有效,
console或event路由调用无效
自定义 CorsMiddleware 中间件为什么 header 不生效
常见错误是把 $response->header() 写在 return $next($request) 之后,或者中间件注册顺序太靠后,导致响应已被日志、异常等中间件提前输出。
-
if ($request->isOptions()) { return response('', 204); }必须放在handle()方法最开头,不能延后 - 所有
$response->header()必须在return $next($request)之前调用,例如:$response = $next($request); $response->header(...); return $response; - 中间件类必须放在
app/middleware.php数组首位,例如:[\app\middleware\CorsMiddleware::class, ...] - 若需支持凭据,
Access-Control-Allow-Origin必须从$request->header('origin')动态读取并校验,不能硬写*,且必须同步设Access-Control-Allow-Credentials: true
用 topthink/think-cors 插件要注意什么
官方插件省事,但配置稍不注意就会失效,尤其在带 Cookie 场景下。
立即学习“PHP免费学习笔记(深入)”;
- 安装后,
config/cors.php中的'allowed_origins'不能含*,必须列全协议+域名+端口,例如:['https://admin.example.com', 'http://localhost:5173'] -
'supportsCredentials' => true和'allowed_origins'必须同时配置,缺一不可;设了true却留*白名单,浏览器直接拒收 - 插件默认放行所有路径,生产环境务必改
'paths' => ['api/*'],避免管理后台或静态资源被意外跨域暴露 - 中间件注册位置仍要置顶:
\think\middleware\Cors::class必须在app/middleware.php第一个
真正容易被忽略的是:所有方案都依赖 Origin 请求头存在且未被 Nginx/Apache 过滤。如果用了反向代理,记得检查是否透传了 Origin 头,否则中间件里读到的是空值,白名单校验永远失败。



















