Laravel跨域问题核心卡点是中间件挂载与生效、Nginx拦截OPTIONS请求。HandleCors须置于$middleware数组顶部(非api组内),supports_credentials为true时allowed_origins禁用通配符,Nginx需透传OPTIONS并返回204。

直接上结论:Laravel 跨域问题不是配个 config/cors.php 就能解决的,核心卡点在中间件是否挂载、是否生效、以及是否被 Nginx 拦截了 OPTIONS 请求。
HandleCors 中间件没生效?先看注册位置和顺序
绝大多数“配置写了但没反应”的情况,是中间件根本没执行。Laravel 9+ 虽已内置 Fruitcake\Cors\HandleCors,但它默认只加在 $middlewareGroups['api'] 里——这意味着只有走 Route::middleware('api') 的路由才受控。
- 如果你用的是
Route::get('/api/data', ...)但没套middleware('api')组,HandleCors压根不跑 - 若想全局生效(比如还要放行
/sanctum/csrf-cookie),必须手动加到$middleware数组顶部(不是底部) - 检查
app/Http/Kernel.php中是否误删了\Fruitcake\Cors\HandleCors::class,尤其升级后容易被清理掉 - Laravel 8 及以下必须
composer require fruitcake/laravel-cors,否则config/cors.php是废文件
allowed_origins 设为 ['*'] 却报错 A wildcard '*' cannot be used...?
这是 supports_credentials 和 origin 冲突的典型表现——只要你在前端 fetch 里写了 credentials: 'include',或后端启用了 'supports_credentials' => true,allowed_origins 就绝不能是 ['*']。
- 开发时写成
['http://localhost:3000']或['http://127.0.0.1:3000'](注意协议、域名、端口全匹配) - 生产环境必须列明所有合法前端域名,如
['https://app.example.com', 'https://admin.example.com'] - 别依赖
allowed_origins_patterns做模糊匹配,它只在 Laravel 9.2+ 支持,且正则写错就彻底失效 - 如果用了 Sanctum,还要确认
SESSION_DOMAIN是.example.com(带前导点),否则 Cookie 不会随跨域请求发出
OPTIONS 请求返回 404 或 500?Nginx 比 PHP 先动手了
浏览器发预检请求时,如果 Nginx 没把 OPTIONS 转发给 PHP-FPM,Laravel 根本收不到请求,HandleCors 更无从执行——此时你在 Laravel 日志里都看不到这条请求。
- 在 Nginx 配置里加这段,确保 OPTIONS 透传:
location ~ ^/(api|sanctum)/ { try_files $uri $uri/ /index.php?$query_string; 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' 'Content-Type,Authorization,X-Requested-With' always; add_header 'Access-Control-Allow-Credentials' 'true' always; if ($request_method = 'OPTIONS') { add_header 'Access-Control-Max-Age' 86400; return 204; } } - 不要只靠 Laravel 返回 200,Nginx 层提前响应 204 更高效、更可靠
- Apache 用户需确认
mod_headers和mod_rewrite已启用,并在.htaccess或虚拟主机中显式允许 OPTIONS
自定义中间件里用 ->header() 链式调用?小心被覆盖
在中间件 handle() 里写 return $next($request)->header(...) 看似简洁,但 Laravel 后续中间件(比如异常处理、响应格式化)可能重写响应头,导致 CORS 头丢失。
- 正确做法是操作
$response->headers对象:$response = $next($request); $response->headers->set('Access-Control-Allow-Origin', $origin); $response->headers->set('Access-Control-Allow-Credentials', 'true'); return $response; - 别在
handle()里直接return response()->header(...),这会跳过整个响应生命周期 - 如果要动态判断 origin(比如白名单查数据库),务必在
handle()开头就做,避免走到$next($request)后再补头
真正难搞的从来不是“怎么加头”,而是 OPTIONS 请求有没有进到 PHP、allowed_origins 和 supports_credentials 是否逻辑自洽、Nginx 和 Laravel 的头设置有没有打架——这三个点漏一个,跨域就稳稳失败。


















