Nginx 拦截 OPTIONS 预检导致 GET 请求未到达 Laravel,query 参数并非丢失而是请求未转发;需在 Nginx location 块中显式处理 OPTIONS 并确保 GET 完整透传至 PHP-FPM,同时校验 Laravel CORS 路径匹配与 credentials 配置。

GET 请求跨域时参数没丢,但你收不到——问题不在参数本身,而在预检(OPTIONS)被 Nginx 拦截后,后续 GET 请求压根没进 Laravel。
为什么浏览器发了 GET 却收不到 query 参数
这不是 Laravel 解析失败,而是请求根本没到达你的 PHP 代码。典型现象:前端 URL 是 https://api.example.com/v1/users?id=123&name=test,但 request()->query() 返回空数组,$_GET 也是空的。
- Nginx 在收到 OPTIONS 预检后,若未显式放行,会直接返回 204,且不把后续 GET 转发给 PHP-FPM
- 哪怕你配了
fruitcake/laravel-cors,它只处理已进入 Laravel 的请求;Nginx 拦下的请求,Laravel 压根看不见 - 部分 Nginx 配置把
location /和location ~ \.php$分开,导致带 query string 的 GET 被错误路由或丢弃
Nginx 必须在 location 块里处理 OPTIONS 并透传 GET
不能只靠 Laravel 中间件,必须在 Nginx 层确保预检通过、且真实请求完整转发。重点不是加头,是“别拦”。
- 确认你的
location ~ ^/api/(或对应路径)块中包含完整的 OPTIONS 处理逻辑,而不是只写在server级 -
add_header不继承,写在server里对location无效 - GET 请求必须能匹配到
fastcgi_pass或proxy_pass,且 query string 未被rewrite截断或覆盖
示例正确配置片段:
location ~ ^/api/ {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Headers "Content-Type, Authorization";
add_header Access-Control-Allow-Methods "GET, POST, OPTIONS";
add_header Access-Control-Allow-Credentials "true";
add_header Access-Control-Max-Age "86400";
return 204;
}
<pre class="brush:php;toolbar:false;"># 关键:确保 GET 请求能正常进 PHP,且保留 $args
try_files $uri $uri/ /index.php?$query_string;
}
Laravel 的 cors.php 配置容易忽略的细节
即使 Nginx 放行了请求,Laravel 的 CORS 配置仍可能让 query 参数“不可见”——不是丢,是被中间件提前终止或覆盖。
-
'paths' => ['api/*']必须精确匹配你实际访问的路径,比如请求是/v1/users,但你只写了api/*,而路由没走api.php,中间件就不生效 -
'supports_credentials' => true时,'allowed_origins'不能是['*'],否则浏览器拒绝发送 Cookie 和部分 header,可能导致 Laravel 认证中间件跳过,连带 request 对象初始化异常 - 如果你用的是 Sanctum,且路由没加
middleware('auth:sanctum')或漏了web中间件组里的 session 启动,request()可能处于半初始化状态,query()不可用
验证是否真丢了参数的快速定位法
别急着改代码,先分层确认数据在哪一层消失:
- 在 Nginx access log 里查该 GET 请求的原始 URI:如果 log 里显示
"GET /api/users?id=123 HTTP/1.1",说明参数已到 Nginx 层 - 在 Laravel 的
public/index.php开头加file_put_contents('/tmp/debug.log', print_r($_SERVER, true), FILE_APPEND);,看$_SERVER['QUERY_STRING']是否存在 - 在控制器第一行加
Log::info('Raw query: ' . request()->fullUrl());,对比输出是否含参数
最常被忽略的是:你以为在调试 Laravel,其实卡在 Nginx 的 try_files 或 rewrite 规则里,query string 已被清空或重写。











