0

Nginx rewrite重定向参数过滤方法:选择性保留和剔除查询参数的实战配置

2026.05.29 | youres | 5次围观

写在前面

做网站运维的朋友,或多或少都遇到过这种场景:用户在访问某个带查询参数的 URL 时,需要把他重定向到新地址,但查询参数里有些该保留、有些该扔掉。比如 UTM 参数想留着,但分页参数 page 已经没意义了;或者反过来,认证 token 要删掉,但来源页面 id 要保留。

这种「有选择地处理查询参数」的需求,在 Nginx 里用 rewrite 配合几个变量就能实现,不需要写复杂的 Lua 脚本,也不必借助第三方模块。本文把几种常见场景和对应的配置方法逐个讲清楚。

先搞清楚 Nginx 对查询参数的基本处理逻辑

Nginx 的 rewrite 指令在执行重定向时,对查询参数(问号后面的部分)有一套默认行为:

  • 不带问号的 rewrite:目标 URL 会自动附加原始查询参数(如果存在的话)。比如 rewrite ^/old /new redirect;,访问 /old?foo=bar 会跳到 /new?foo=bar
  • 带问号的 rewrite:问号后面的内容会替换掉原始查询参数。如果没有写内容,等同于清空所有查询参数。

这个默认逻辑是「过滤参数」的基础——理解它,才能知道该加问号还是不该加。

方法一:直接清空所有查询参数

最简单的一种:重定向目标不需要任何原始参数,手动清空即可。

rewrite ^/campaign/old$ /campaign/new? redirect;
# 访问 /campaign/old?utm_source=xxx&page=2 会跳到 /campaign/new(无参数)

注意末尾的问号——它明确告诉 Nginx:不要附加原始查询字符串。这个写法比下面这种写法更直观:

rewrite ^/campaign/old$ /new redirect;
# 这个写法会保留 ?utm_source=xxx&page=2,不是你想要的

小结:想清空参数,加一个 ? 就够了。

方法二:用正则捕获选择性保留参数

有时候我们想保留部分参数,而不是全部保留或全部丢弃。通过正则捕获组可以精准提取目标参数。

假设场景:旧产品页面路径变了,但只保留 ref 参数,扔掉 utm_source 和其他所有参数。

set $target_ref '';
if ($args ~* "(^|&)ref=([^&]+)") {
    set $target_ref $2;
}

if ($target_ref != '') {
    return 301 /product/current?utm_ref=$target_ref;
}
return 301 /product/current;

通过正则捕获 ref 参数的值,赋给变量 $target_ref,然后在 redirect 时重新拼接成新的查询字符串。要保留其他参数时,在正则里多写几个捕获组即可。

方法三:扔掉特定参数,保留其余(参数白名单思路)

有时候场景反过来:要保留大多数参数,只删除某几个「脏」参数,比如包含敏感信息的参数。思路是用正则把要删除的参数从查询字符串里「抠」出去。

# 删除 session_id 参数,保留其余
if ($args ~* "(^|&)session_id=[^&]+") {
    set $args_temp $args;
    # 用非捕获组配合正则替换,删除 session_id=xxx 这一段
    set $args_clean $args;
}

# 更好的方式:直接用 map 指令(见方法四)

在 rewrite 里直接操作 $args 变量时要注意:if 条件为真才执行 set,条件为假时变量保持原值,不会自动清零。这导致用 if+set 做参数删除时容易有残留 bug。更稳妥的做法是用 map 指令。

方法四:用 map 指令实现参数过滤(生产环境推荐)

对于需要频繁处理参数过滤的场景,用 map 指令把参数处理逻辑搬到 http 块里,配置更清晰,高并发下性能也更好。

场景:旧系统参数名是 productid,新系统统一用 id,同时删除所有 debug 参数。

http {
    # 提取 productid/id 参数值
    map $args $product_id {
        ~*(^|&)productid=([^&]+)    $2;
        ~*(^|&)id=([^&]+)           $2;
        default                    '';
    }

    # 删除 debug 参数
    map $product_id $final_product_id {
        default                    $product_id;
    }

    server {
        location /item {
            # 判断是否有 productid/id 参数,有则重定向
            if ($product_id != '') {
                set $args_with_id "id=$product_id";
                return 301 /products/$product_id?;
            }
            return 301 /products;
        }
    }
}

map 指令在 Nginx 启动时构建一张哈希表,请求进来时直接查表,不需要每次都跑正则匹配。对于高并发场景,这是最推荐的做法。

方法五:用 $is_args$args 精确控制参数附加行为

rewrite 目标 URL 可以用 $is_args$args 两个内置变量来精确控制是否附加原始查询参数:

  • $is_args:原始请求有查询参数时值为 ?,否则为空字符串。
  • $args:原始查询参数字符串(不含问号)。
# 只保留前两个参数(适用于参数固定顺序的场景)
if ($args ~ ^([^&]+&[^&]+)(&.*)?$) {
    set $args_two $1;
}

rewrite ^/old$ /new$is_args$args_two? permanent;

这条规则通过正则捕获前两个 key=value 对,其余全部丢弃。末尾的 ? 确保如果前两步没有匹配到任何参数,不会出现多余的问号。

实战:完整配置示例

把几种方法综合起来,看一个真实场景的完整配置——旧版搜索页面迁移到新版,保留搜索关键词,丢弃分页和过滤参数:

server {
    listen 80;
    server_name example.com;
    
    # 提取搜索关键词
    set $search_kw '';
    if ($args ~* "(^|&)q=([^&]+)") {
        set $search_kw $2;
    }

    location /search/legacy {
        # 只保留 q 参数,丢弃 page / sort / filter 等
        if ($search_kw != '') {
            return 301 /search?q=$search_kw;
        }
        return 301 /search;
    }
}

访问 /search/legacy?utm_source=google&sort=price&page=3 时会正确识别搜索关键词,其余参数全部过滤,最终只跳到 /search?q=

踩坑提醒

  • 问号加与不加,效果完全不同。rewrite 目标末尾加 ? 表示清空参数,不加则附加原参数。这是很多人踩的第一个坑。
  • if 内部 set 变量有条件。只有在 if 条件为真时,set 才会执行。条件为假时变量保持原值,不会自动清零。
  • rewrite 正则的贪婪匹配。.* 匹配参数时默认是贪婪的,小心捕获组位置错位。
  • map 放在 http 块内。map 指令是 HTTP 级别的,不能放在 server 或 location 块里。
  • 先 curl 验证再上线。curl -I 查看返回的 Location 头,确认参数处理正确。

总结

Nginx rewrite 做参数过滤,核心就是两件事:问号控制是否清空,以及 $args / $is_args / 正则捕获 组合实现选择性保留。日常场景用前两种方法足够,复杂或高频场景建议上 map 指令。

实际配置中,建议先用 curl 验证跳转结果是否符合预期,再全量上线:

curl -I "https://example.com/old-path?foo=bar&debug=1&q=test"
# 查看返回的 Location 头,确认参数处理正确

参数处理对了,SEO 权重传递和用户访问体验才有保障。

相关推荐

版权声明

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

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