前言:为什么你的Nginx rewrite总是丢参数?
很多同学在配置Nginx rewrite规则时都会遇到一个头疼的问题:重定向后查询参数(query string)丢失了。比如用户访问 /search?q=nginx,重写后变成了 /new-search,参数 q=nginx 不见了。
这个问题看似简单,但如果不搞清楚Nginx rewrite处理查询参数的底层机制,很容易在复杂规则中踩坑。今天这篇文章,我会从问题本质、4种解决方法、5种常见场景三个维度,帮你彻底搞懂Nginx rewrite如何保留查询参数。
一、问题本质:为什么rewrite会丢失查询参数?
Nginx的rewrite指令在处理URL时,默认行为是这样的:
- 如果重写后的URL中不包含查询字符串,Nginx默认不会自动保留原始查询参数
- 如果重写后的URL中包含了
?,Nginx会认为你要自定义查询字符串,此时原始参数会被丢弃(除非你显式引用$args或$query_string)
来看一个典型错误示例:
# 错误示例:参数会丢失
rewrite ^/search$ /new-search last;
# 访问 /search?q=nginx 会被重写为 /new-search(参数丢失)
这就是很多人踩坑的根源:rewrite后的URL没有显式处理查询参数,Nginx就不会自动带上。
二、方法1:使用$args或$is_args(最常用)
Nginx提供了两个内置变量来处理查询参数:
$args:完整的查询字符串(不含?),例如q=nginx&page=1$is_args:如果存在查询参数,值为?;否则为空字符串
利用这两个变量,可以在rewrite时保留原始查询参数:
# 方法1a:直接拼接$args
rewrite ^/search$ /new-search?$args last;
# 方法1b:使用$is_args自动判断是否加?
rewrite ^/search$ /new-search$is_args$args last;
区别:
/new-search?$args:即使原始请求没有查询参数,也会变成/new-search?(末尾多一个无意义的?)/new-search$is_args$args:如果原始请求没有查询参数,结果就是/new-search(干净整洁)
推荐使用方法1b($is_args$args),避免无意义的末尾?。
三、方法2:使用$request_uri(保留完整原始请求URI)
如果你希望完全保留用户原始请求的URI(包含查询参数),可以使用$request_uri变量:
# 方法2:使用$request_uri保留完整原始请求
rewrite ^ /new-search$request_uri last;
$request_uri的值包含完整的原始请求URI(含查询参数),例如:
- 用户访问
/search?q=nginx&page=1 $request_uri的值为/search?q=nginx&page=1
这种方法的优势是完全不需要关心参数拼接逻辑,适合需要保留原始请求完整信息的场景(如反向代理、API转发)。
⚠️ 注意:$request_uri在rewrite执行过程中不会被更新,它始终是客户端发来的原始URI。如果你在rewrite规则中多次重写,需要用$uri配合$args。
四、方法3:手动拼接参数(精确控制)
如果你需要在保留原始参数的同时,添加额外的查询参数,可以手动拼接:
# 方法3:保留原始参数,并添加新参数
rewrite ^/search$ /new-search?$args&source=rewrite last;
# 访问 /search?q=nginx 会被重写为 /new-search?q=nginx&source=rewrite
这种方式适合需要传递额外上下文信息的场景,比如A/B测试、来源追踪等。
五、方法4:使用return替代rewrite(推荐新写法)
从Nginx 0.8.42开始,return指令支持301/302重定向,并且默认会保留查询参数:
# 方法4:使用return 301/302(默认保留参数)
location /search {
return 301 /new-search$is_args$args;
}
return和rewrite的核心区别:
return:默认保留查询参数(更符合直觉)rewrite:默认不保留查询参数(容易踩坑)
如果你的需求是HTTP 301/302重定向,建议优先使用return,而不是rewrite。关于这两者的详细区别,可以参考我的另一篇文章:Nginx rewrite和return区别详解:重定向到底该用哪个?。
六、5种常见场景完整配置示例
场景1:旧URL重写到新URL(保留参数)
# 旧搜索页面重写到新搜索页面,保留查询参数
rewrite ^/old-search$ /new-search$is_args$args last;
场景2:带参数的URL重写到不带参数的新URL
# 将 /search?q=xxx 重写到 /s/xxx(参数转为路径)
if ($args ~ "q=(.+)") {
set $query $1;
rewrite ^/search$ /s/$query? last;
}
⚠️ 注意:这种场景需要将查询参数提取为变量,再拼接到新URL路径中。
场景3:HTTP跳转HTTPS时保留参数
# HTTP跳转HTTPS,保留查询参数
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
使用$request_uri可以完美保留原始请求的所有信息(路径+参数)。
场景4:重写时添加固定参数
# 重写到新URL,并添加固定参数
rewrite ^/download$ /new-download?$args&ref=rewrite last;
场景5:使用map指令动态决定是否保留参数
# 使用map动态控制是否保留参数
map $args $final_args {
"" "";
default "?$args";
}
server {
rewrite ^/search$ /new-search$final_args last;
}
这种方式适合需要根据条件动态控制参数保留逻辑的复杂场景。
七、调试技巧:如何确认参数是否保留成功?
写完rewrite规则后,如何确认查询参数确实被保留了?这里推荐3个调试方法:
方法1:查看Nginx日志
在nginx.conf的日志格式中添加$args:
log_format debug '$uri args=$args';
access_log /var/log/nginx/access.log debug;
然后访问URL,查看日志中args=后面是否有参数。
方法2:使用curl查看重定向后的URL
# -L 跟随重定向,-v 显示详细过程
curl -L -v http://example.com/search?q=nginx
在输出中查找Location:头,确认重定向后的URL是否包含参数。
方法3:使用Nginx echo模块打印变量
location /debug {
echo "args: $args";
echo "request_uri: $request_uri";
}
访问/debug?test=123,查看输出是否正确。
八、总结:4个关键点记住就够了
- rewrite默认不保留参数:必须显式使用
$args或$is_args - 优先用
$is_args$args:避免无意义的末尾? - HTTP跳转HTTPS用
$request_uri:一步到位保留完整原始请求 - 301/302重定向优先用
return:默认保留参数,更符合直觉
如果你在配置过程中遇到参数丢失的问题,可以按照这个思路排查:
- rewrite后的URL是否包含
??如果包含,是否显式引用了$args? - 是否误用了
$uri(不含参数)而不是$request_uri(含参数)? - 如果使用return,是否漏写了
$is_args$args?
关于Nginx rewrite和return的详细对比,可以参考:Nginx return重定向不保留参数原因:彻底搞清楚301跳转参数丢失的本质。
关于$request_uri和$uri的区别,可以参考:Nginx $request_uri和$uri区别详解:搞懂这两个变量,重定向再也不踩坑。
参考资料:
- Nginx官方文档 - ngx_http_rewrite_module
- Nginx内置变量手册 - $args, $is_args, $request_uri
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论