tp6跨域中间件生效需确保:cors中间件前置注册且控制器不手动设cors头;多域名需自定义中间件动态匹配origin;options预检需启用route::crossdomain()或路由选项;credentials为true时origin不能为*且nginx须透传origin头。

ThinkPHP 6 的跨域中间件怎么配才生效
直接说结论:TP6 默认的 Cors 中间件只在「未设置响应头」时才自动注入,如果控制器里手动调用了 header() 或写了 response()->header(),它就彻底失效——这是最常踩的坑。
正确做法是全程交由中间件控制,别在控制器里碰 CORS 相关 header。中间件默认配置路径是 app/middleware.php,需确认已注册:
return [
\think\middleware\Cors::class,
];
-
Cors中间件必须放在路由/控制器执行前(即靠前位置),否则可能被后续逻辑覆盖 - 它只处理
OPTIONS预检请求和实际接口响应,但不会重写你已写死的Access-Control-Allow-Origin - 若用
Response::create()->header(...)显式设过 origin,中间件会跳过,且不报错、不警告
如何让 Access-Control-Allow-Origin 支持多个域名或通配符
TP6 的 Cors 中间件默认只允许 * 或单个域名,不支持数组形式的多域名白名单。想动态匹配,得自己扩展中间件,不能只改配置。
常见错误是往 app/middleware.php 里传数组参数,比如:
[\think\middleware\Cors::class => ['origin' => ['https://a.com', 'https://b.com']]]
这完全无效——Cors 类根本不解析这种格式。真正能生效的方式只有两种:
- 把
origin设为'*'(仅限不含 credentials 的场景) - 重写中间件,在
handle()里读取请求头Origin,手动比对白名单并写入响应头 - 若需带 cookie,
origin必须是精确字符串(不能是*),且必须同步开启Access-Control-Allow-Credentials: true
为什么 OPTIONS 预检总 404 或没返回 CORS 头
TP6 默认不自动响应 OPTIONS 请求,除非你显式定义了对应路由,或启用了 Route::crossDomain()(仅 TP6.1+)。很多项目漏掉这步,导致浏览器预检失败,但控制台只显示「Network Error」,不提示具体原因。
解决方式很简单,但必须做:
- 在
route/app.php顶部加use think\Route; - 在所有 API 路由外层包一层
Route::crossDomain(true); - 或者为每个资源路由单独加
->option(['cors' => true])(TP6.0 不支持,仅 6.1+) - 确认没有全局
Route::any()拦截了OPTIONS,它会绕过中间件
credentials 为 true 时跨域仍失败的隐藏条件
开了 withCredentials: true 却始终报错?大概率是 Access-Control-Allow-Origin 值为 *,而浏览器强制拒绝。TP6 的 Cors 中间件在检测到 credentials 为 true 时,会自动把 origin 回退为请求头里的原始值——但这只在中间件能拿到 Origin 的前提下成立。
容易忽略的点:
- Nginx/Apache 反向代理时,若没透传
Origin头(如漏了proxy_set_header Origin $http_origin;),TP6 收不到该字段,就会 fallback 到*,触发浏览器拦截 - 前端发请求时,
Origin是浏览器自动加的,但本地文件(file://)或某些测试工具不会发,导致后端无法判断来源 -
Access-Control-Allow-Credentials: true必须和Access-Control-Allow-Origin同时存在,且后者不能是*;少一个,浏览器就静默失败
跨域不是开关一开就完事,每个环节都可能断在 header 透传、路由识别、中间件顺序这些细节上。尤其上线后经 Nginx,最容易丢 Origin 头——先抓包看请求里有没有,再查响应头里回没回对,比瞎调配置快得多。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











