高并发下Laravel跨域需Nginx拦截OPTIONS返回204、HandleCors置全局中间件栈顶部、动态校验Origin并设SESSION_DOMAIN为.example.com、Sanctum域名与CORS白名单一致。

高并发下 Laravel 的跨域配置不能只靠 config/cors.php 一配了事,核心矛盾是:预检请求(OPTIONS)在流量洪峰时被 Nginx 拦截或 PHP 层排队阻塞,导致前端大量 404/503;同时 supports_credentials => true 与动态域名白名单的组合极易引发会话失效或头冲突。
HandleCors 中间件必须挂载到全局中间件栈顶部
很多人把 \Fruitcake\Cors\HandleCors::class 塞进 $middlewareGroups['api'],结果高并发时部分请求根本没走 API 组(比如 /sanctum/csrf-cookie 或健康检查接口),预检直接失败。更糟的是,若中间件在栈中靠后,前面的认证/限流中间件可能已抛出异常,CORS 头压根不会写入响应。
- 务必加到
app/Http/Kernel.php的$middleware数组最上方(不是底部),确保所有 HTTP 请求都经过它 - Laravel 9+ 内置该类,但不会自动注册——检查是否被升级脚本误删
- 避免和自定义 CORS 中间件共存,否则头重复或覆盖,浏览器拒绝解析
Nginx 必须透传 OPTIONS 并返回 204,不交给 PHP 处理
每秒上千次预检请求打到 PHP-FPM,会迅速吃光 worker 进程,造成雪崩。正确做法是让 Nginx 在七层直接响应,不转发给 Laravel。
- 在 server 块内加:
if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin "$http_origin" always; add_header Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS" always; add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,Content-Type,Authorization" always; add_header Access-Control-Allow-Credentials "true" always; add_header Access-Control-Expose-Headers "X-RateLimit-Remaining,X-RateLimit-Limit" always; add_header Access-Control-Max-Age 86400 always; return 204; } -
always参数不可省——否则 Nginx 默认不给 204 响应加 header - 不要用
add_header Access-Control-Allow-Origin "*"配合supports_credentials => true,Nginx 会硬塞 *,浏览器直接拒收 - 前端发请求时带
Origin头,Nginx 就原样回传,既安全又免去 PHP 解析开销
allowed_origins 不能写死,要用运行时校验
高并发场景下,前端可能来自多个子域(app.example.com、admin.example.com)、CDN 回源(10.0.1.12)、甚至灰度环境(staging.example.com:3000)。全写进 config/cors.php 不仅难维护,还因配置缓存导致变更延迟生效。
- 在
config/cors.php中把allowed_origins设为空数组:[] - 改用中间件动态判断:
public function handle($request, Closure $next) { $origin = $request->headers->get('Origin'); $allowed = ['https://app.example.com', 'https://admin.example.com', 'https://staging.example.com']; if ($origin && in_array($origin, $allowed)) { $response = $next($request); $response->headers->set('Access-Control-Allow-Origin', $origin); $response->headers->set('Access-Control-Allow-Credentials', 'true'); return $response; } return response()->json(['message' => 'Forbidden'], 403); } - 注意:动态判断必须放在
HandleCors之前执行,否则它的默认逻辑会覆盖你设的头 - 别依赖
allowed_origins_patterns,正则匹配在高并发下有性能损耗,且 Laravel 9.2+ 才支持
supports_credentials 和 SESSION_DOMAIN 必须协同生效
一旦开启凭证支持,跨域请求才能携带 Cookie,但这也意味着:后端 session 无法识别、CSRF token 失效、登录态丢失——问题往往不出在 CORS 配置,而在 Cookie 属性和域名设置。
-
SESSION_DOMAIN必须设为.example.com(带前导点),否则浏览器不会把 Cookie 发给子域 - 确保
config/session.php中'secure' => true(生产环境 HTTPS 下必须),且'same_site' => 'lax'或'none'(后者需同时开secure) - 前端 fetch 必须显式写
credentials: 'include',且不能和mode: 'no-cors'混用 - Sanctum 的
stateful域名列表(config/sanctum.php)要和 CORS 白名单完全一致,否则 CSRF cookie 不下发
真正卡住高并发跨域的,从来不是配置项写错,而是 OPTIONS 请求没被 Nginx 拦截、动态 origin 校验没绕过中间件顺序、或者 Cookie 的 Domain 和 SameSite 属性在负载均衡后出现不一致。这些点不逐个验证,光调 config/cors.php 永远只是隔靴搔痒。


















