Laravel 11.x 默认内置 HandleCors 中间件,无需安装 fruitcake/laravel-cors;需将其置于 $middleware 数组顶部(紧随 TrustProxies 后),发布并精准配置 config/cors.php(如 supports_credentials=true 时 allowed_origins 禁用 '*'),并确保 Nginx 透传 OPTIONS 请求返回 204。

Laravel 11.x 默认已内置 Illuminate\Http\Middleware\HandleCors 中间件,无需安装 fruitcake/laravel-cors 包(该包在 Laravel 9+ 后已弃用,Laravel 11 完全移除对其兼容支持)。配置核心在于三件事:启用中间件、正确放置位置、精准填写 config/cors.php。
✅ 正确启用 HandleCors 中间件
打开 app/Http/Kernel.php,确认 HandleCors 已注册在 全局 $middleware 数组顶部附近(不是 api 组内,也不是 $routeMiddleware):
protected $middleware = [
\Illuminate\Foundation\Http\Middleware\TrustProxies::class,
\Illuminate\Http\Middleware\HandleCors::class, // ✅ 必须在此处,紧随 TrustProxies 之后
\Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance::class,
// ... 其他中间件
];
⚠️ 注意:
- 不要只加在
'api'中间件组里(如$middlewareGroups['api']),否则/sanctum/csrf-cookie或/login等非 API 路由会跨域失败; - 不要加在
$routeMiddleware里再手动->middleware('cors'),Laravel 11 不再预定义该别名; - 如果删过或注释了这行,补上并清缓存:
php artisan config:clear。
✅ 发布并配置 config/cors.php
运行命令生成配置文件(即使 Laravel 11 内置,仍需此步自定义):
php artisan vendor:publish --tag=cors
编辑 config/cors.php,关键项必须按需设准:
return [
'paths' => ['api/*', 'sanctum/csrf-cookie', 'login', 'logout'], // 明确需要跨域的路径
'allowed_methods' => ['*'], // 或显式写 ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS']
'allowed_origins' => ['http://localhost:3000', 'https://your-app.com'], // ❌ 禁止用 ['*'](若启 credentials)
'allowed_origins_patterns' => [],
'allowed_headers' => ['*'], // 若含自定义头(如 `X-Api-Version`),建议显式列出
'exposed_headers' => ['X-RateLimit-Remaining', 'X-RateLimit-Reset'], // 前端需读取的响应头
'supports_credentials' => true, // ✅ 需传 Cookie / Token 时必须为 true
'max_age' => 3600,
'origin_is_regex' => false,
];
? 关键规则:
-
supports_credentials => true时,allowed_origins*绝对不可含 `''`**,浏览器会直接拒绝请求; - 开发环境写死
http://localhost:3000,生产环境只列可信域名(协议 + 域名 + 端口); -
paths要覆盖所有需跨域的端点(例如 Sanctum 的 CSRF、登录接口等)。
✅ Nginx 层必须透传 OPTIONS 请求
Laravel 11 的 HandleCors 依赖 PHP 层处理预检(OPTIONS),但 Nginx 默认拦截 OPTIONS 并返回 405。需在 server 块中添加:
# 在 location ~ \.php$ {} 或通用 location 块内
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 "Content-Type, Authorization, X-Requested-With" always;
add_header Access-Control-Allow-Credentials "true" always;
add_header Access-Control-Max-Age "86400" always;
add_header Content-Length "0" always;
add_header Content-Type "text/plain; charset=utf-8" always;
return 204;
}
同时确保主 PHP 处理块中未屏蔽 OPTIONS:
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
# ✅ 不要加类似 if ($request_method !~ ^(GET|HEAD|POST|PUT|DELETE|OPTIONS)$) { return 405; }
}
✅ 前端配合要点(常被忽略)
- fetch 请求必须带
credentials: 'include'(与后端supports_credentials => true对应); - 若用 Axios,设置
withCredentials: true; - 检查浏览器控制台 Network → Options 请求的响应头是否含
Access-Control-Allow-Origin、Access-Control-Allow-Credentials、Access-Control-Allow-Methods—— 少一个就失败。
不复杂但容易忽略。











