thinkphp跨域核心解法:路由级用allowcrossdomain()自动处理options并支持凭证;自定义中间件须拦截options返回204、动态校验origin、透传header;credentials启用时origin不可为*,且需nginx透传origin头。

ThinkPHP跨域问题不是“配个 header 就行”,真正卡住的往往是 OPTIONS 预检没响应、凭证(credentials)和通配符 * 冲突、或中间件执行顺序错位。直接上有效解法。
路由级跨域:只对 API 路由开,最安全
ThinkPHP 6 推荐优先用 allowCrossDomain(),它不污染全局,且自动处理预检逻辑,比手写中间件更少出错。
- 必须链式写在路由定义末尾,比如
Route::get('api/user', 'Api/User')->allowCrossDomain(['https://admin.example.com'], true) - 第二个参数
true表示启用凭证(带 Cookie / Authorization),此时第一个参数不能是*,必须是明确域名数组 - 该方法仅对 HTTP/HTTPS 路由生效,
console或event路由不会触发 - 如果前端发的是
PUT或带Content-Type: application/json的请求,浏览器会先发 OPTIONS;allowCrossDomain()内部已自动拦截并返回 204,不用额外处理
自定义 CORS 中间件:全站统一控制,但必须拦 OPTIONS
全局中间件看似简单,但漏掉 OPTIONS 处理,前端就收不到响应头,控制台报 405 Method Not Allowed 或静默失败。
- 在
app/middleware/CorsMiddleware.php的handle()开头加判断:if ($request->isOptions()) { return response('', 204); } - 不要用
header()函数,TP6 使用 PSR-7 响应对象,应调$response->header(),例如:$response->header('Access-Control-Allow-Origin', 'https://a.com') -
Access-Control-Allow-Headers必须包含前端实际发送的字段,比如Authorization和Content-Type,否则预检失败 - 注册时把该中间件放在
app/middleware.php数组最前面,确保它最早执行、最晚结束,避免被其他中间件覆盖响应头
credentials + Origin 冲突:浏览器直接拒绝,不是后端没设
前端配了 credentials: 'include',后端却设 Access-Control-Allow-Origin: *,浏览器会直接丢弃响应,连控制台都不报 CORS 错误——这是最隐蔽的坑。
- 启用凭证时,后端
Access-Control-Allow-Origin必须是具体域名,比如https://admin.example.com,不能是*,也不能是空字符串 - 如需多域名支持,得动态读取请求头
Origin并白名单校验,再回写,例如:$origin = $request->header('origin'); if (in_array($origin, ['https://a.com', 'https://b.com'])) { $response->header('Access-Control-Allow-Origin', $origin); } - ThinkPHP 默认 session cookie 不跨子域,若前端是
admin.example.com、后端是api.example.com,需在config/session.php中设'domain' => '.example.com'(注意开头的点) - JWT 场景下,前端带
Authorization: Bearer xxx,后端Access-Control-Allow-Headers必须显式包含Authorization
Nginx + Swoole 多层代理时 Origin 头丢失
本地开发 localhost:3000 → Nginx → Swoole,经常发现后端收不到 Origin 头,导致白名单校验失效或 Access-Control-Allow-Origin 写错。
- Nginx 配置里必须透传 Origin:
proxy_set_header Origin $http_origin;,否则 Swoole 收到的$request->header('origin')是空的 - 如果用了 Cloudflare 或其他 CDN,也要确认它们没有过滤或重写
Origin头 - 调试时可在中间件里
dump($request->header())直接看原始头,别只信浏览器 Network 面板里的 Request Headers
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











