前言:一个问号引发的血案
写Nginx重定向规则的时候,你有没有遇到过这种情况:明明配置了rewrite,跳转后URL里的查询参数却凭空消失了?utm_source、page、id这些参数,一个都不剩。折腾半天,最后发现问题出在rewrite规则里一个小小的问号(?)上。
今天这篇文章,我就把Nginx rewrite中问号的含义彻底讲清楚,让你以后写重定向规则不再踩坑。
一、rewrite指令的基本语法
先复习一下rewrite的基本格式:
rewrite regex replacement [flag];
三个部分:
- regex:正则表达式,匹配请求的URI(不含查询参数)
- replacement:替换后的目标地址
- flag:可选,控制后续处理行为(last、break、redirect、permanent)
关键点在于:rewrite只处理$uri部分,不包含问号后面的查询字符串。查询参数的附加,需要你显式处理。
二、问号在replacement中的特殊含义
这是本文的核心。在rewrite的replacement部分,问号有两个截然不同的行为:
1. 没有问号 → 自动追加原查询参数
如果你的replacement里不包含问号,Nginx会自动把原始请求的查询参数追加到目标URL后面。
# 请求: /old?page=2&sort=date
rewrite ^/old$ /new last;
# 结果: /new?page=2&sort=date ← 参数自动保留了!
这是Nginx的默认行为,很多人不知道,所以有时候参数"莫名其妙"还在。
2. 有问号 → 清空原查询参数,使用新的
一旦replacement里出现了问号,Nginx就认为你要自己控制查询参数,于是丢弃所有原始参数,只保留问号后面的内容。
# 请求: /old?page=2&sort=date
rewrite ^/old$ /new?status=ok last;
# 结果: /new?status=ok ← 原参数page=2&sort=date全没了!
这就是参数丢失的最常见原因——你无意中在replacement里加了个问号,原参数就被清空了。
三、问号后面什么都不写:彻底清空参数
有时候我们确实想清空所有查询参数,怎么做?
rewrite ^/old$ /new? last;
# 请求: /old?page=2&sort=date
# 结果: /new ← 干干净净,没有问号,没有参数
问号后面什么都不跟,Nginx会把原始查询参数全部丢弃,目标URL也不会带问号。
这种用法在规范化URL时很有用——去掉无意义的跟踪参数,让URL更干净。
四、保留原参数的正确姿势
既然问号会清空原参数,那想保留原参数该怎么做?有三种常用方法:
方法1:不在replacement中写问号
# 最简单,不写问号就自动追加
rewrite ^/old$ /new last;
方法2:用$is_args$args拼接
当你需要在replacement中写问号,又想保留原参数时:
rewrite ^/old$ /new?status=ok$is_args$args last;
# 请求: /old?page=2
# 结果: /new?status=ok&page=2
这里解释一下:
- $is_args:如果原始请求有查询参数,值为
?;没有则为空 - $args:原始请求的查询参数值(不含问号)
所以$is_args$args就是"有参数就带上?加参数,没参数就什么都不加"。
方法3:用$request_uri直接取完整路径
如果是HTTP跳HTTPS这种场景,用return 301更简单:
return 301 https://$host$request_uri;
$request_uri包含完整的URI和查询参数,一个变量搞定。
五、常见坑与实战案例
坑1:rewrite带问号导致UTM参数丢失
# 错误写法
rewrite ^/blog/(.*)$ /article?slug=$1 last;
# 请求: /blog/hello?utm_source=wechat
# 结果: /article?slug=hello ← utm_source没了!
# 正确写法
rewrite ^/blog/(.*)$ /article?slug=$1&$args last;
# 或者更安全的写法
rewrite ^/blog/(.*)$ /article?slug=$1$is_args$args last;
坑2:问号位置不当导致URL格式异常
# 危险:当原始请求没有参数时
rewrite ^/old$ /new?$args last;
# 请求: /old(无参数)
# 结果: /new? ← 末尾多了个问号
# 安全:用$is_args判断
rewrite ^/old$ /new$is_args$args last;
坑3:多次rewrite参数被反复覆盖
如果配置了多条rewrite规则,每条带问号的rewrite都会重新设定查询参数:
rewrite ^/a$ /b?x=1 last; # 第1次:参数变为x=1
rewrite ^/b$ /c?y=2 last; # 第2次:参数变为y=2,x=1丢了
遇到这种情况,需要在每一步都手动拼接参数,或者改用set指令来累积参数。
六、总结:一张表搞懂问号行为
| replacement写法 | 原始参数 | 结果URL | 说明 |
|---|---|---|---|
| /new | ?page=2 | /new?page=2 | 无问号,自动追加原参数 |
| /new?status=ok | ?page=2 | /new?status=ok | 有问号,原参数被丢弃 |
| /new? | ?page=2 | /new | 问号后为空,彻底清空参数 |
| /new?status=ok$is_args$args | ?page=2 | /new?status=ok&page=2 | 手动拼接,保留原参数 |
| /new$is_args$args | (无参数) | /new | 无参数时不带问号 |
相关文章推荐
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论