0

Nginx $is_args和$args组合用法详解:重定向保留查询参数的正确姿势

2026.05.27 | youres | 7次围观

为什么要搞清楚 $is_args 和 $args 的组合

做Nginx重定向的时候,查询参数丢失是最常见的坑之一。很多人知道用 $request_uri 能保留完整路径,但有些场景你只需要保留参数部分,或者要对参数做过滤、拼接、修改——这时候就得靠 $is_args$args 组合出击了。

这篇文章把这两个变量怎么搭配用、什么场景用、容易踩什么坑,一次讲清楚。

$is_args 和 $args 分别是什么

$is_args:问号本身

$is_args 的值只有两种可能:

  • 请求URL中有查询参数时,值为 ?
  • 请求URL中没有查询参数时,值为空字符串 ""

举个例子:

# 请求:https://example.com/page?keyword=nginx
$is_args = "?"

# 请求:https://example.com/page
$is_args = ""

$args:问号后面的内容

$args 存的是查询字符串的完整内容,不包含问号:

# 请求:https://example.com/page?keyword=nginx&page=2
$args = "keyword=nginx&page=2"

# 请求:https://example.com/page
$args = ""

组合使用的核心逻辑

两个变量组合起来,就能在重定向时安全地拼接查询参数

# 拼接公式
$uri$is_args$args

# 有参数时:/page?keyword=nginx&page=2
# 无参数时:/page

关键点在于 $is_args 自动处理了"有没有问号"这个问题——有参数它就补问号,没参数它就不补,不会出现 /page? 这种末尾拖个问号的情况。

5个实战场景

场景1:HTTP跳转HTTPS保留查询参数

最经典的应用,把HTTP请求301跳转到HTTPS,同时保留所有查询参数:

server {
    listen 80;
    server_name example.com;

    # 方法1:用 $request_uri(最简单)
    return 301 https://$host$request_uri;

    # 方法2:用 $uri$is_args$args(更灵活)
    return 301 https://$host$uri$is_args$args;
}

两种方法效果一样,但方法2更灵活——比如你只想保留特定参数时,方法1就做不到了。

场景2:重定向时追加新参数

跳转的同时需要加一个新参数(比如UTM追踪标记):

# 请求:/old-page?keyword=nginx
# 目标:/new-page?keyword=nginx&ref=migration

location /old-page {
    if ($args) {
        return 301 /new-page?${args}&ref=migration;
    }
    return 301 /new-page?ref=migration;
}

注意这里的判断逻辑:原来有参数,就用 & 连接新参数;原来没参数,新参数直接用 ? 开头。

场景3:重定向时过滤掉指定参数

跳转后想去掉某些参数(比如内部调试参数 debug),保留其他参数:

# 用 map 过滤参数
map $args $filtered_args {
    ~^(.*?)(?:&|^)debug=[^&]*(?:&(.*))?$ $1$2;
    default $args;
}

server {
    # 跳转时用过滤后的参数
    location /old {
        return 301 /new$uri$is_args$filtered_args;
    }
}

这种参数过滤用 $request_uri 是做不了的,必须拆开来处理。

场景4:跨域名重定向保留参数

从一个域名跳到另一个域名,带上原始查询参数:

server {
    listen 80;
    server_name old-domain.com;

    return 301 https://new-domain.com$uri$is_args$args;
}

这样 old-domain.com/search?q=nginx 就会跳转到 new-domain.com/search?q=nginx,参数完整保留。

场景5:日志中记录完整请求路径

自定义日志格式,把完整路径(含参数)记下来:

log_format detailed '$remote_addr - $remote_user [$time_local] '
                     '"$request" $status $body_bytes_sent '
                     '"$http_referer" "$http_user_agent" '
                     'uri=$uri$is_args$args';

access_log /var/log/nginx/detailed.log detailed;

这样日志里就能看到每个请求的完整路径和参数,排查问题特别方便。

常见坑和避坑指南

坑1:末尾多出问号

如果你不用 $is_args,直接写 ?$args

# 错误写法
return 301 https://$host$uri?$args;

# 请求 /page(无参数)时,跳转到 /page?,末尾多了个问号

虽然功能上不影响,但对SEO不友好,搜索引擎可能把 /page?/page 当成两个不同的URL。用 $uri$is_args$args 就不会出现这个问题。

坑2:rewrite中问号的特殊含义

rewrite 指令中,替换字符串里的 ? 会导致原来的参数被丢弃:

# 这条rewrite会把原始参数全部丢弃
rewrite ^/old(.*)$ /new$1?redirected=true permanent;

# 正确做法:用 $is_args$args 保留原始参数
rewrite ^/old(.*)$ /new$1$is_args$args&redirected=true permanent;

这是 Nginx rewrite 的设计:替换字符串中出现 ? 就表示"用新的查询字符串替换旧的"。

坑3:$args 在 rewrite 后会变

$args 的值在 rewrite 执行后不会改变——它始终是原始请求的查询字符串。如果你在 rewrite 中修改了参数,新的参数会出现在 $query_string 里,但 $args 不受影响。

# $args 始终是原始的查询字符串
# $query_string 在 rewrite 后可能不同
# 实际上 $args 和 $query_string 是同一个东西,都不会因 rewrite 改变

纠正一下:$args$query_string 其实是同一个变量的两个名字,值完全一样。如果你在 rewrite 的替换部分用 ? 设了新参数,新参数不会自动出现在 $args 里——它们会出现在最终的重定向URL中,但 $args 变量还是原始值。

$is_args$args 和 $request_uri 的选择

场景推荐用原因
简单跳转保留全部参数$request_uri代码最简洁
需要修改/追加参数$is_args$args可以灵活操作参数部分
需要过滤掉某些参数$is_args$args$request_uri 无法拆分参数
只需要路径不需要参数$uri$uri 不含查询字符串
日志记录$uri$is_args$args可读性好,字段拆分清晰

相关文章

版权声明

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

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