0

Nginx rewrite保留查询参数完整教程

2026.05.26 | youres | 17次围观

前言:为什么你的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个关键点记住就够了

  1. rewrite默认不保留参数:必须显式使用$args$is_args
  2. 优先用$is_args$args:避免无意义的末尾?
  3. HTTP跳转HTTPS用$request_uri:一步到位保留完整原始请求
  4. 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辅助作者原创,未经许可,转载请保留原文链接。

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