ThinkPHP 6 默认不发CORS头,手动设header易失效;最稳方案是自定义中间件,须处理OPTIONS预检、动态校验Origin白名单、避免credentials与*冲突,并确保Nginx透传Origin头。

ThinkPHP 6 默认不发任何 CORS 响应头,光写 header() 或在控制器里调 response()->header() 很容易失效——因为框架底层可能已发送响应,或被后续中间件覆盖。最稳的方案是用中间件,且必须正确处理 OPTIONS 预检请求。
为什么路由上加 ->allowCrossDomain() 有时没反应
这个方法只对 HTTP 路由生效,且必须写在路由定义链的末尾;如果用了分组路由但没把中间件显式绑定进去,或者路由被 Route::any() 拦截了 OPTIONS,它就完全不触发。更关键的是:它不自动处理凭证(withCredentials: true)场景下的 Origin 校验,一旦前端带 Cookie,而你传了 ['*'],浏览器直接拒绝响应。
-
->allowCrossDomain(['https://a.com'], true)才启用凭证支持,且origin数组里不能含* - 若域名带端口(如
http://localhost:3000),必须完整写入白名单,少一个字符都不匹配 - 它不拦截 OPTIONS 请求,而是依赖后续中间件响应;如果全局没配 Cors 中间件,预检会 405 或静默失败
手写 CorsMiddleware 的三个硬性条件
官方 think\middleware\Cors 类虽存在,但参数逻辑反直觉、不校验 Origin 白名单、也不适配带凭证场景。自己写一个反而更可控,重点盯住三件事:
- 开头用
$request->isOptions()拦住预检请求,并return response('', 204)—— 返回空体 + 204 状态码,比 200 更规范 - 非 OPTIONS 请求,在
$next($request)之后再操作$response,否则头可能被覆盖;用$response->header()设置,别用原生header() -
Access-Control-Allow-Origin必须和Access-Control-Allow-Credentials: true互斥于*:要么不用凭证,要么动态读$request->header('origin')做白名单比对后回写
Nginx 反向代理下 Origin 头丢失怎么办
常见现象是本地开发正常,部署到 Nginx + Swoole 后跨域失败,curl 查看响应头发现压根没 Access-Control-Allow-Origin。大概率是 Nginx 把 Origin 头过滤了,导致 PHP 层无法识别来源,也就不敢回写对应头。
立即学习“PHP免费学习笔记(深入)”;
- 在 Nginx 的
location块里加:proxy_set_header Origin $http_origin; - 同时确保
proxy_pass_request_headers on;(默认是 on,但有些精简配置会关掉) - Swoole 模式下还要检查是否重复写响应头:比如中间件设了一次,Swoole 自动又加了一次,触发
CORS header ‘Access-Control-Allow-Origin’ cannot be used with wildcard错误
真正麻烦的从来不是加几行头,而是 Origin 动态校验、凭证开关联动、反向代理透传、以及中间件执行顺序这四点卡在一起——漏一个,前端控制台就只报 “No 'Access-Control-Allow-Origin' header”,但实际原因可能藏在 Nginx 配置里。



















