Laravel 8 仅靠 PHP 层配置 CORS 不够,必须在 Nginx 层显式处理 OPTIONS 预检请求,否则浏览器收不到 CORS 响应头;需在 location 块内用 if ($request_method = 'OPTIONS') 返回 204 并设置一致的 Access-Control- 头,且 allowed_origins 不可为 (若启用 credentials)。

在 Laravel 8 中,仅靠 PHP 层配置 CORS(比如用 fruitcake/laravel-cors)往往不够——如果 Nginx 拦截了浏览器发起的 OPTIONS 预检请求,Laravel 根本收不到该请求,自然不会返回任何 CORS 响应头。所以必须在 Nginx 层显式支持 OPTIONS,并透传或直接响应预检。
Nginx 必须处理 OPTIONS 请求
Laravel 不处理 OPTIONS 请求本身(它只处理被路由匹配到的实际请求),而浏览器在发送带认证、自定义 Header 或非简单方法(如 PUT/DELETE)的跨域请求前,会先发一个无 body 的 OPTIONS 请求。若 Nginx 没配置响应这个请求,就会返回 405、403 或直接超时,前端报错如 “Failed to fetch”、“Network Error” 或控制台显示 “OPTIONS xxx cancelled”。
- 在对应 API 的
location块内添加if ($request_method = 'OPTIONS')分支 - 该分支必须返回
204 No Content(不能是 200 + 空 body,否则某些客户端可能出错) - 所有
add_header必须写在该if块内部,且与实际接口响应头保持一致(尤其Access-Control-Allow-Origin和Access-Control-Allow-Credentials)
推荐的 location 配置示例(适用于 /api/ 路由)
将以下内容放入站点配置文件中(如 /etc/nginx/conf.d/your-site.conf 的 server 块内):
location ^~ /api/ {
# 正常代理到 Laravel(PHP-FPM)
try_files $uri $uri/ /index.php?$query_string;
<pre class="brush:php;toolbar:false;"># 处理预检请求
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "https://your-frontend.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With, X-CSRF-TOKEN";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Max-Age "86400";
add_header Content-Length 0;
add_header Content-Type text/plain;
return 204;
}
# 对正常请求也添加 CORS 头(可选,但建议与 OPTIONS 一致)
add_header Access-Control-Allow-Origin "https://your-frontend.com";
add_header Access-Control-Allow-Methods "GET, POST, PUT, DELETE, OPTIONS";
add_header Access-Control-Allow-Headers "Content-Type, Authorization, X-Requested-With, X-CSRF-TOKEN";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Expose-Headers "Content-Length, X-Request-ID";}
-
Access-Control-Allow-Origin不能写*(当启用了 credentials 时);开发环境可填http://localhost:3000,生产环境务必写具体域名 -
Access-Control-Allow-Headers必须包含前端实际发送的头,比如带 Token 就要含Authorization,用 Sanctum CSRF 就要含X-CSRF-TOKEN - 避免把
add_header写在server或http块顶层——Nginx 的 header 不继承,只对当前location生效
配合 Laravel 的关键检查点
Nginx 配好后,Laravel 层仍需协同设置,否则仍可能失败:
- 确保
config/cors.php中'supports_credentials' => true时,'allowed_origins'是明确域名数组(如['https://your-frontend.com']),绝不可为['*'] - 确认
app/Http/Kernel.php的$middlewareGroups['api']包含\Fruitcake\Cors\HandleCors::class(Laravel 8 默认已配,但升级后可能被删) - 路由必须定义在
routes/api.php,且未被手动覆盖中间件(例如没加->middleware('web')) - 执行
php artisan config:clear清除配置缓存,避免旧配置干扰
替代方案:用 Nginx 反向代理彻底规避跨域
如果 CORS 配置反复出问题,更稳妥的做法是让前后端“同源”:
- 前端部署在
https://app.example.com - Nginx 将
/api/路径反向代理到 Laravel 后端(如http://127.0.0.1:8000/api/) - 前端 AJAX 请求仍发往
https://app.example.com/api/xxx,浏览器视为同源,无需 CORS
这种方式不依赖任何 CORS 响应头,也绕开了 OPTIONS 问题,适合生产环境长期稳定运行。











