问题背景:为什么查询参数总是丢?
配置 Nginx 重定向时,很多人遇到过这样的怪事:访问 /search?q=nginx,跳转后变成了 /new-search,参数 q=nginx 不见了。
或者更糟:参数被重复拼接了两次,变成 ?q=nginx?q=nginx。
根本原因是:Nginx 的 return 和 rewrite 在处理查询参数时的行为完全不同,而大多数人并不知道 $is_args 和 $args 这两个变量的存在。
核心变量:先搞懂这三个东西
在讲最佳实践之前,先把三个核心变量搞清楚:
$args:原始的查询字符串(不含 ?)
$args 的值是纯查询字符串,不包含前导的 ?。例如请求 /test?a=1&b=2,则 $args 的值是 a=1&b=2。
$is_args:条件问号,有参数才是 ?,否则是空字符串
$is_args 是 Nginx 设计的一个非常巧妙的变量:
- 当请求有查询参数时,
$is_args的值是? - 当请求没有查询参数时,
$is_args的值是空字符串""
这个设计的意义是:让你可以安全地拼接 URL,不用担心多一个或少一个 ?。
$request_uri:完整的原始请求 URI(含参数)
$request_uri 是浏览器发送的完整 URI,包含路径和查询字符串,且不会被 Nginx 内部重写改变。适合用于整体跳转,不适合做参数拼接。
最佳实践一:return 重定向时保留原参数
使用 return 做重定向时,Nginx 默认不会自动保留原始查询参数。必须手动拼接。
错误写法(参数会丢失)
location /old-path {
return 301 /new-path;
}
访问 /old-path?ref=home,跳转后变成 /new-path,参数 ref=home 丢失。
正确写法(保留原参数)
location /old-path {
return 301 /new-path$is_args$args;
}
这样拼接后:
- 有参数时:
/new-path?ref=home - 无参数时:
/new-path(不会多一个多余的?)
最佳实践二:rewrite 重定向时的参数行为
rewrite 的行为和 return 不同:
rewrite ... permanent/redirect(带 flag):默认会保留原始查询参数rewrite替换字符串中带?:会覆盖原始参数
默认保留参数的写法
rewrite ^/old/(.*)$ /new/$1 last;
此时原始参数会自动附加到新 URI 后面,不需要手动拼接 $args。
需要覆盖参数时的写法
rewrite ^/old/(.*)$ /new/$1?src=rewrite last;
这里的 ? 后面的内容会完全替换原始查询参数。如果还想保留原始参数,需要显式拼接:
rewrite ^/old/(.*)$ /new/$1?src=rewrite&$args last;
最佳实践三:拼接新参数同时保留旧参数
实际场景中经常需要在保留原始参数的同时,追加自己的参数:
location /checkout {
rewrite ^ /checkout?from=redirect&$args last;
}
这样原始参数和新增参数会同时生效,例如原始请求 /checkout?user=123,重写后的效果是 /checkout?from=redirect&user=123。
最佳实践四:proxy_pass 场景下的参数传递
使用 proxy_pass 转发请求时,查询参数的处理也有讲究:
默认行为(推荐)
location /api/ {
proxy_pass http://backend/;
}
默认情况下,proxy_pass 会完整传递原始请求的 URI(含查询参数),大多数场景直接用就够了。
需要重写路径时
location /api/ {
rewrite ^/api/(.*)$ /$1 break;
proxy_pass http://backend;
}
注意:proxy_pass 后面不带 / 时,Nginx 会使用当前 URI(含 rewrite 后的结果)进行转发,包括查询参数。
最佳实践五:避免参数重复拼接的陷阱
最常见的错误是在 return 里同时用了 $is_args$args 和 $request_uri:
# 错误:会导致参数重复或路径错误 return 301 https://example.com$request_uri$is_args$args;
$request_uri 已经包含了查询参数,再拼 $is_args$args 就会重复。
正确写法二选一:
# 写法一:用 $request_uri(整体跳转,最简单) return 301 https://example.com$request_uri; # 写法二:手动拼接(需要修改路径时用) return 301 /new-path$is_args$args;
最佳实践六:条件判断中拼接参数的正确方式
在 if 指令中做重定向时,同样需要注意参数拼接:
if ($args ~ "ref=newsletter") {
return 301 /welcome$is_args$args;
}
注意:Nginx 的 if 是 EvAl 语义,避免在 if 里写复杂逻辑。能用 map 就用 map。
最佳实践七:用 map 过滤或改写查询参数
如果需要选择性保留某些参数、丢弃其他参数,map 是最干净的方式:
map $args $filtered_args {
default $args;
}
location / {
rewrite ^ /target$is_args$filtered_args last;
}
更复杂的需求可以配合 set $new_args 来构造新的参数字符串。
调试技巧:确认参数是否拼接正确
在确认配置正确之前,可以先用 add_header 把拼接结果打到响应头里观察:
location /debug {
add_header X-Debug-Args "$args" always;
add_header X-Debug-Is-Args "$is_args" always;
add_header X-Debug-Request-Uri "$request_uri" always;
return 200 "check headers";
}
用 curl 查看:
curl -I "https://www.youres.cn/debug?test=1"
总结:记住这 4 条就够了
- return 重定向:必须手动拼
$is_args$args,否则参数丢失 - rewrite 默认行为:外部重定向(带 flag)默认保留参数;替换字符串带
?则覆盖参数 - 不要用 $request_uri 再拼 $args:两者都包含参数,会重复
- $is_args 是条件问号:有参数时是
?,没有时是空字符串,用它可以避免多拼一个?
相关阅读:
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论