必须显式拦截并返回204:thinkphp中间件需在handle()开头用$request->isoptions()判断并立即return response('', 204),不可走$next()流程;内置cors类须严格写为think\middleware\cors::class且置于middleware.php首位;带credentials时access-control-allow-origin不能为*,须动态校验origin白名单;nginx必须透传origin头,否则校验失效。

ThinkPHP 中间件必须显式处理 OPTIONS 请求
浏览器发起带 Authorization、Content-Type: application/json 或自定义 Header 的跨域请求时,会先发一个 OPTIONS 预检请求。如果中间件没拦截它,而是直接交给后续逻辑(比如路由或控制器),就会因找不到匹配路由返回 404,或被 Session/JWT 中间件拒绝而报 500/401——根本不是接口写错了,是预检被漏掉了。
关键点在于:OPTIONS 请求必须在中间件里「提前截断并返回」,不能走 $next($request) 流程。否则响应头无法设置,或者 headers already sent。
-
if ($request->isOptions()) { return response('', 204); }是最简放行方式,必须放在handle()方法开头 - 不能只设响应头然后继续
$next()—— 那样OPTIONS还是会往下走,可能触发路由 404 - 返回状态码推荐
204(No Content),比200更符合规范,且不带 body 可避免某些代理异常
think\middleware\Cors::class 注册位置和写法要严格
ThinkPHP 6/8 内置的 think\middleware\Cors::class 确实能自动处理 OPTIONS,但前提是它得被正确注册:位置靠前、类名完整、参数合法。
- 必须写成
think\middleware\Cors::class,写成'cors'、CorsMiddleware::class或别名都会失效 - 在
app/middleware.php的全局中间件数组中,它应排在第 1~2 位;若后面跟着SessionMiddleware或 JWT 验证中间件,预检请求可能被提前终止 - 多应用模式下,得去对应应用目录下的
app/middleware.php配,根目录配置不生效 - 如果用了
think-swoole或workerman,内置Cors中间件可能因底层不兼容 header 设置而失效,此时必须手写中间件用$response->header()
带 credentials 时,Access-Control-Allow-Origin 不能是 *
前端设置了 credentials: 'include',但浏览器控制台报 “Credentials flag is true, but the 'Access-Control-Allow-Origin' value is not the literal '*'”,说明后端响应头冲突了——这是浏览器强制限制,绕不过。
-
Access-Control-Allow-Origin和Access-Control-Allow-Credentials: true必须同时存在,且前者不能为* - 必须动态读取
$request->header('origin'),再比对白名单数组,匹配成功才设具体域名,否则干脆不设(让浏览器 fallback 到默认拒绝) - 大小写敏感:
https://Admin.example.com和https://admin.example.com视为不同源,白名单要完全一致 - 本地开发常用
http://localhost:3000,但生产环境务必删掉这个,只留真实域名
NGINX 透传 Origin 头是隐性前提
即使 PHP 层中间件写对了,Nginx 如果没把 Origin 请求头转发进来,$request->header('origin') 就是空的,白名单校验永远失败,Access-Control-Allow-Origin 只能退化成 *,进而导致 credentials 场景直接挂掉。
- 检查 Nginx 配置里是否有
proxy_set_header Origin $http_origin;(反向代理场景) - 确认没有
underscores_in_headers off;导致带横线的 header(如X-Requested-With)被丢弃 - 用
curl -H "Origin: https://admin.example.com" -I http://your-api.com/test直连 PHP-FPM,绕过 Nginx 测试是否正常——能通说明是 Nginx 问题
Origin 头在 Nginx 和 PHP 之间断了,或者 credentials 和 * 被同时写死——这两个点不排查清楚,调十遍中间件也没用。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











