ThinkPHP 8.0 跨域需满足四点:预检拦截、Origin 动态校验、Credentials 配合、响应头写入时机;推荐路由级 allowCrossDomain() 或自定义 CorsMiddleware,禁用 * 通配 Origin(启凭证时),且须正确处理 OPTIONS 请求与响应头覆盖问题。

ThinkPHP 8.0 设置跨域访问头必须同时满足浏览器 CORS 协议和凭证传递规则,不能只加几个 header 就完事。关键在于预检(OPTIONS)拦截、Origin 动态校验、Credentials 配合、响应头写入时机这四点。
路由级快速启用(适合 API 分组)
在 route/app.php 中对特定路由分组启用,最轻量且优先级高:
- 用 Route::group() 包裹接口,末尾链式调用 ->allowCrossDomain(),例如:
Route::group('api/v1', function () { Route::get('user', 'Api/User@index'); })->allowCrossDomain(['https://admin.example.com', 'http://localhost:5173'], true); - 第二个参数 true 表示启用 Cookie / Authorization 凭证,此时
Access-Control-Allow-Origin必须是具体域名,不能是* - 该方式只作用于 HTTP 路由,不生效于 console 或 event 类型路由
自定义 CorsMiddleware(推荐全局统一控制)
手动创建中间件,精准拦截 OPTIONS 并注入响应头,避免插件配置遗漏或覆盖:
修复 Core bailout 状态损坏安全问题,以及 BZ2、DOM、PDO、Opcache/JIT 内存与执行错误,改进 Unicode 字素处理、URL 解析、TLS 流状态、信号分发及会话错误处理。
- 执行命令生成:
php think make:middleware CorsMiddleware - 在 app/middleware/CorsMiddleware.php 的
handle()开头判断并响应预检:if ($request->isOptions()) { return response('', 204) ->header('Access-Control-Allow-Origin', $origin) ->header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS') ->header('Access-Control-Allow-Headers', 'Authorization,Content-Type,X-Requested-With,X-Token') ->header('Access-Control-Allow-Credentials', 'true'); } -
$origin 必须先校验:从
$request->header('origin')获取,并比对白名单数组(如['https://admin.example.com', 'http://localhost:5173']),不匹配则不设Access-Control-Allow-Origin - 普通请求的响应头,要在
$response = $next($request)之后、return $response之前设置
使用 think-cors 插件(适合标准化项目)
通过官方扩展快速集成,但需注意默认配置陷阱:
立即学习“PHP免费学习笔记(深入)”;
- 安装:
composer require topthink/think-cors(PHP ≥ 8.0) - 检查 config/cors.php,重点修改:
–'paths' => ['api/*', 'v1/*', '/login'](必须显式包含所有跨域接口路径,通配不覆盖根路径)
–'allowed_origins' => ['https://admin.example.com', 'http://localhost:5173'](禁用['*'],尤其带 Cookie 时)
–'supports_credentials' => true(启用凭证后,allowed_origins必须为具体域名) - 确认 app/middleware.php 中
\think\middleware\AllowCrossDomain::class在全局中间件数组首位
注意事项与常见踩坑
很多失败不是配置没写,而是时机或逻辑错位:
-
OPTIONS 不拦截 → 浏览器卡死:中间件里没写
if ($request->isOptions()) { return ... },会导致框架走完整流程,可能返回 405 或无 CORS 头 -
Header 被覆盖:控制器中用了
return json(...)或response()->json(...),会重置响应头;务必确保 CORS 头在最终响应对象上写入 -
Allow-Headers 不匹配:前端发了
X-Auth-Token,但中间件只写了Authorization,Content-Type,预检直接失败;必须大小写一致、无空格、无多余逗号 -
Cookie 场景下 Origin 不能动态 fallback 到 *:哪怕开发环境用 localhost,也要明确列进白名单,不可写成
$origin ?: '*'后直接设头


















