ThinkPHP 8跨域需同时满足CORS与Cookie凭证要求:使用think-cors插件或手写CorsMiddleware,配置allowed_origins禁止用*,中间件须置全局首位;路由级可调用allowCrossDomain();Cookie需设SameSite=None且Secure,前后端credentials与Origin必须严格匹配。

ThinkPHP 8项目需同时满足浏览器CORS协议与Cookie凭证传递要求,否则前端fetch设置credentials: 'include'后仍会因Access-Control-Allow-Origin为*或缺失Access-Control-Allow-Credentials头而被拒绝响应,真实请求甚至不会发出。
使用think-cors插件快速启用(推荐生产环境)
执行安装命令:composer require topthink/think-cors,PHP版本必须≥8.0。
安装完成后检查config/cors.php是否生成,重点修改三项:
【'paths' => ['api/*', 'v1/*']】:若接口路径含/v1/或/api/前缀,保留通配;若存在根路径/login或/admin接口,必须显式加入,否则中间件不触发。
立即学习“PHP免费学习笔记(深入)”;
【'allowed_origins' => ['https://admin.example.com', 'http://localhost:5173']】:只要前端带Cookie或Authorization头,此处绝不能写['*'],必须列明完整协议+域名+端口,否则浏览器直接拦截。
确认app/middleware.php中已将\think\middleware\AllowCrossDomain::class置于全局中间件数组首位——顺序错位会导致日志或异常中间件提前输出响应,header被覆盖。
手写CorsMiddleware中间件(调试/动态Origin必需)
第一步:运行命令生成中间件文件:php think make:middleware CorsMiddleware。
第二步:打开app/middleware/CorsMiddleware.php,清空handle方法内容,填入以下逻辑:
if ($request->isOptions()) {
return response('', 204)
->header('Access-Control-Allow-Origin', $request->header('origin') ?: '*')
->header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
->header('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, X-Token')
->header('Access-Control-Allow-Credentials', 'true');
}
第三步:在$response = $next($request)之后、return $response之前插入响应头设置——【header()必须在$next($request)调用之后、return之前执行,否则已被框架内部响应体锁定】。
第四步:将\app\middleware\CorsMiddleware::class添加到app/middleware.php中间件数组最顶部,确保它比日志、Auth等中间件更早执行且更晚注入header。
路由级精准控制(仅对特定接口开放跨域)
方法一:在route/app.php中对单个路由启用
Route::post('login', 'api/Auth@login')->allowCrossDomain(['https://admin.example.com'], true);
方法二:对整个API分组启用
Route::group('api/v1', function () {
Route::get('users', 'api/User@index');
Route::post('login', 'api/Auth@login');
})->allowCrossDomain(['https://admin.example.com', 'http://localhost:3000'], true);
注意:allowCrossDomain()必须链式挂在Route::group()末尾,写在闭包内部或漏掉括号会导致配置丢失。
方法三:若前端使用axios且默认携带Authorization头,但预检失败,需在allowCrossDomain()参数中显式补全Headers——插件默认只放Content-Type和X-Requested-With,缺Authorization就会卡在OPTIONS阶段。
Cookie安全配置补全(前后端必须协同)
第一步:后端设置Cookie时强制SameSite=None; Secure
cookie('token', $value, [ 'expire' => 3600, 'path' => '/', 'domain' => '.example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'None' ])
第二步:前端fetch请求必须带credentials: 'include'
fetch('/api/v1/user', { method: 'GET', credentials: 'include' })
第三步:后端响应头中Access-Control-Allow-Origin值必须与请求头Origin完全一致,不可截断或忽略端口——例如前端是http://localhost:5173,就不能只配http://localhost。
第四步:若使用Swoole或Workerman部署,禁用所有php原生header()函数,全部改用$response->header()方式,否则报Cannot modify header information。



















