写在前面
做网站运维的朋友,或多或少都遇到过这种场景:用户在访问某个带查询参数的 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辅助作者原创,未经许可,转载请保留原文链接。

发表评论