0

Nginx 查询参数拼接最佳实践:$is_args、$args、$request_uri 的正确用法

2026.05.28 | youres | 9次围观

问题背景:为什么查询参数总是丢?

配置 Nginx 重定向时,很多人遇到过这样的怪事:访问 /search?q=nginx,跳转后变成了 /new-search,参数 q=nginx 不见了。

或者更糟:参数被重复拼接了两次,变成 ?q=nginx?q=nginx

根本原因是:Nginx 的 returnrewrite 在处理查询参数时的行为完全不同,而大多数人并不知道 $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 条就够了

  1. return 重定向:必须手动拼 $is_args$args,否则参数丢失
  2. rewrite 默认行为:外部重定向(带 flag)默认保留参数;替换字符串带 ? 则覆盖参数
  3. 不要用 $request_uri 再拼 $args:两者都包含参数,会重复
  4. $is_args 是条件问号:有参数时是 ?,没有时是空字符串,用它可以避免多拼一个 ?

相关阅读:

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论
883文章数 0评论数
作者其它文章