Laravel跨域问题主因是中间件未生效、OPTIONS预检被拦截或supports_credentials与allowed_origins冲突;Laravel 9.2+已内置HandleCors,需将其置于$middleware顶部(紧接TrustProxies后),配置中allowed_origins不可为['*'](当supports_credentials=true时),且Nginx必须透传OPTIONS请求。

报错通常不是配置写错了,而是中间件没跑起来、预检被拦了、或者凭据和通配符硬冲突。Laravel 9.2+ 已内置 HandleCors,老项目用 fruitcake/laravel-cors,但配置逻辑完全一致,重点在三点:注册位置、supports_credentials 与 allowed_origins 的搭配、以及 OPTIONS 请求是否畅通。
中间件必须挂对位置才能生效
它不能只加在 $middlewareGroups['api'] 里——那样只管带 middleware('api') 的路由。如果你的接口没套这个组(比如直接 Route::get('/api/data', ...)),中间件根本不会执行。
- 全局生效:把
\Illuminate\Http\Middleware\HandleCors::class(Laravel 9.2+)或\Fruitcake\Cors\HandleCors::class(Laravel 8 及以下)加到$middleware数组顶部,紧接在\App\Http\Middleware\TrustProxies::class后面 - API 路由专用:确保它出现在
$middlewareGroups['api']中,且位置靠前,别被StartSession或Auth类中间件挡住 - 别塞到底部或末尾——顺序错等于没加
allowed_origins 和 supports_credentials 别同时乱设
浏览器强制要求:只要启用了凭据(credentials: 'include' 或后端设 'supports_credentials' => true),allowed_origins 就绝不能是 ['*']。否则直接报 A wildcard '*' cannot be used。
- 开发环境写死:例如
['http://localhost:3000', 'http://127.0.0.1:3000'];http://localhost:*不合法,端口不能通配 - 生产环境必须收敛:只列可信域名,如
['https://app.example.com', 'https://admin.example.com'] - 如果不需要传 cookie 或 token,可设
'supports_credentials' => false,此时allowed_origins才能用['*']
OPTIONS 预检请求常被意外拦截
Laravel 不自动注册 OPTIONS 路由,预检失败往往不是 CORS 配置问题,而是中间件提前返回了 405 或 401。
- 确认目标路径在
config/cors.php的'paths'里,例如['api/*', 'sanctum/csrf-cookie'] - 检查该路由没被
auth:sanctum、auth:api等认证中间件包裹——预检请求不带 token,认证中间件会直接拒掉 - Nginx 层要透传 OPTIONS:加
if ($request_method = 'OPTIONS') { add_header Access-Control-Allow-Origin '*'; add_header Access-Control-Allow-Methods 'GET, POST, PUT, DELETE, OPTIONS'; add_header Access-Control-Allow-Headers 'Content-Type, Authorization'; return 204; },避免被服务器直接返回 405
调试时盯住这三组响应头
单看 Access-Control-Allow-Origin 不够,浏览器需要凑齐至少三项才放行:
-
Access-Control-Allow-Origin:值必须匹配前端 origin,且不能是*(当凭据开启时) -
Access-Control-Allow-Methods:列出允许的方法,['*']在多数场景可用 -
Access-Control-Allow-Credentials:仅当supports_credentials为 true 时存在,且值必须是true(字符串) - 额外注意:
exposed_headers要包含前端 JS 实际读取的自定义响应头,比如X-RateLimit-Remaining


















