Laravel 9 API 返回403/419或空白响应,主因是VerifyCsrfToken中间件拦截OPTIONS预检请求;需在$except中添加'api/*'放行,确保HandleCors注册于api中间件组,并配置config/cors.php的paths、allowed_origins与supports_credentials匹配。

Laravel 9 中 API 路由返回 403(或 419、空白响应),通常不是 CORS 配置本身的问题,而是 CSRF 保护拦截了 OPTIONS 预检请求——浏览器发完 OPTIONS 就卡住,后续请求根本没发出去,所以你看到的“跨域失败”其实是被中间件提前拦掉了。
下面直击关键点,分三步解决:
✅ 第一步:放行 CSRF 中间件对 API 的拦截
Laravel 默认的 VerifyCsrfToken 中间件会拒绝所有非 GET/HEAD 请求(包括 OPTIONS),而预检请求正是 OPTIONS 方法。
打开 app/Http/Middleware/VerifyCsrfToken.php,在 $except 数组中明确排除 API 路由:
protected $except = [
'api/*',
'sanctum/csrf-cookie',
];⚠️ 注意:只改 cors.php 不生效,CSRF 拦截不解除,CORS 中间件压根没机会运行。
✅ 第二步:确认 CORS 中间件已正确注册
Laravel 9+ 已内置 HandleCors(无需额外安装 fruitcake/laravel-cors),但默认不启用。
检查 app/Http/Kernel.php:
-
若希望仅 API 路由跨域 → 把中间件加到
$middlewareGroups['api']里:'api' => [ \Illuminate\Http\Middleware\HandleCors::class, 'throttle:api', \Illuminate\Routing\Middleware\SubstituteBindings::class, ], 切勿加到全局
$middleware数组(会导致登录页、静态资源也带 CORS 头,可能干扰调试)。
运行命令验证是否生效:
php artisan route:list --name=your-api-route | grep cors
目标路由应显示 handle-cors 或类似中间件标识。
✅ 第三步:检查 config/cors.php 的关键配置项
发布配置(如未生成):
php artisan vendor:publish --tag=cors
重点核对以下三项是否匹配前端行为:
-
'paths' => ['api/*']—— 确保覆盖你的接口路径 -
'allowed_origins' => ['http://localhost:3000', 'https://your-app.com']
→ 如果前端用了credentials: true(如fetch(..., { credentials: 'include' })),*这里不能写 `['']`**,否则浏览器直接拒绝响应 -
'supports_credentials' => true
→ 必须与前端withCredentials一致;同时需确保SESSION_DRIVER=cookie且 Cookie 属性(如SameSite=None; Secure)适配 HTTPS 环境
其他建议:
-
'allowed_headers' => ['Content-Type', 'Authorization', 'X-Requested-With']
→ 显式列出前端实际发送的头,漏掉Authorization会导致带 token 的请求被拒 - 开发时可临时加
'exposed_headers' => ['*']方便调试
? 补充排查:Nginx 是否劫持了 OPTIONS?
如果 php artisan route:list 显示中间件已注册,但 Network 面板里根本看不到 OPTIONS 请求(或显示 cancelled / 405),说明请求没进 PHP 层。
检查 Nginx 配置,在 location ~ ^/api/ 块内添加:
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "*" always;
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE" always;
add_header Access-Control-Allow-Headers "DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age 86400 always;
return 204;
}⚠️ add_header 必须写在 location 块内,server 级别不继承;避免和 PHP 层重复设置导致响应头冲突。
清空缓存再试:
php artisan config:clear php artisan cache:clear
只要这三步到位,403 问题基本消失。核心逻辑是:先让 OPTIONS 进来,再让 CORS 头出去,最后让真实请求被正确响应。


















