一、先搞清楚:rewrite 和 return 各自是什么
在说参数行为差异之前,先把这两个指令的本质搞清楚。
rewrite 是 Nginx 的 URL 重写指令,它在服务器内部对请求的 URI 进行修改之后,继续用修改后的 URI 走后续的过滤链。说白了,它更像是一次"内部改写",浏览器地址栏的 URL 可能不变。
return 是 Nginx 的响应返回指令,它直接向客户端发送一个指定的响应(可以是 301/302 重定向、444 关闭连接,或者直接返回内容)。它是一次性终结处理,不再走后续的 location 匹配。
这个本质区别,是理解两者参数行为差异的根本前提。
二、参数行为差异:这是最容易踩坑的地方
很多运维在实际配置中最困惑的就是:为什么 rewrite 能自动保留 URL 参数,但 return 不行?
2.1 rewrite 默认自动追加原始查询字符串
rewrite 在执行重写之后,Nginx 会自动将原始请求的查询字符串(? 后面的部分)追加到新 URI 后面。你不需要额外做任何操作。
举例:请求 /old-page?id=123&name=test,配置 rewrite ^/old-page /new-page redirect; 之后,Nginx 会自动变成 /new-page?id=123&name=test 再跳转。
这个行为是由 rewrite 指令的内部处理机制决定的——它并不是因为 rewrite 做了什么特殊操作,而是 Nginx 的重写模块默认就这样处理。
2.2 return 默认丢弃原始查询字符串
return 指令的行为完全不同。当你写 return 301 https://example.com/new-page; 时,Nginx 只会跳转到你指定的 URL,不会自动携带原始的查询参数。
请求 /old?id=123,配置 return 301 https://example.com/new; 之后,跳转目标变成了 https://example.com/new,参数完全丢失。
这不是 Nginx 的 bug,而是 return 的设计逻辑——return 跳转的是你指定的具体 URL,不存在"自动追加"这个概念。
三、实战配置对比:5种常见场景
场景一:简单 301 跳转
将旧页面跳转到新页面,要求保留参数。
location /old-page {
return 301 https://example.com/new-page$request_uri;
}
注意这里用了 $request_uri,它包含原始的完整 URI(包含参数),这样跳转后参数就保留下来了。如果不用 $request_uri,而直接写死 URL,参数就会丢失。
场景二:域名统一跳转(HTTPS 强制)
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
这也是 $request_uri 的经典用法——在 return 里手动拼接参数。
场景三:改写 URI 保留参数(内部重写)
location /search {
rewrite ^/search/(.*) /search.php?q=$1 last;
}
rewrite 默认保留原始参数,但如果你在 rewrite 的目标里手动指定了参数(比如 ?q=xxx),Nginx 的行为是用你指定的参数替换原始参数,而不是追加。这里容易出错,需要特别注意。
场景四:return 和 rewrite 混用时的参数行为
server {
listen 80;
server_name example.com;
# 第一步:rewrite 改写 URI,同时保留参数
rewrite ^/(.*) https://example.com/$1 permanent;
# 此时 rewrite 会保留原始参数,跳转到 https://example.com/$1?原始参数
}
但更常见的写法还是直接用 return:
server {
listen 80;
server_name example.com;
return 301 https://example.com$request_uri;
}
场景五:清空参数或替换参数
有时候你需要完全替换 URL 参数,用 rewrite 可以这样做:
location /api {
rewrite ^/api/(.*) /backend.php?src=rewrite&original=$1? last;
}
注意末尾的 ?——Nginx 中 rewrite 目标 URI 末尾加 ? 会清空原始参数,改为只使用目标中的参数。
四、性能对比:哪个更快?
很多运维会问 rewrite 和 return 哪个性能更好。实际上:
- return 性能稍好——它不需要经过重写模块的处理流程,直接返回响应码,在高并发场景下资源消耗更少。
- rewrite 需要经过 rewrite 模块的解析和过滤链,执行路径更长一些。
但在实际生产环境中,这个性能差异微乎其微,基本可以忽略。真正选择哪个,更多取决于功能需求,而不是性能。
五、什么场景用哪个?一张表说清楚
| 需求 | 推荐指令 | 原因 |
|---|---|---|
| 301/302 外部跳转 | return | 语义明确,性能更优 |
| 内部 URI 重写(不改变地址栏) | rewrite | return 无法做内部重写 |
| 返回固定内容或错误码 | return | return 444 关闭连接等特殊操作 |
| URL 改写+继续处理 | rewrite + flag | rewrite 支持 last/break/redirect/permanent |
| 带参数跳转 | return + $request_uri | 手动拼接参数,行为清晰 |
六、容易踩的3个坑
坑1:return 跳转后参数消失,没加 $request_uri
这是最常见的配置错误。很多人以为 return 301 https://example.com/new; 会自动保留参数,实际上不会。正确做法是:
return 301 https://example.com/new$request_uri;
坑2:rewrite 里手动加参数后,原始参数被覆盖
当你在 rewrite 目标中明确写了查询参数时,Nginx 会用你写的参数替换原始参数,而不是两者合并。比如:
rewrite ^/old /new?name=张三 last; # 原始 ?id=123 会被丢弃
如果想合并,需要用 $is_args$args:
rewrite ^/old /new?name=张三$is_args$args last;
坑3:rewrite 的 flag 选错导致死循环
last 和 break 容易混淆:last 会重新走一遍 location 匹配(可能触发循环),break 只在当前 location 内部停止,不再匹配。如果配置不当,容易出现 too many redirects 错误。
七、总结
Nginx 的 rewrite 和 return 在参数处理上的核心差异:
- rewrite:默认自动保留原始查询参数,不需要额外处理。
- return:只跳转到指定 URL,不会自动保留参数,需要手动用
$request_uri或$is_args$args拼接。
选哪个指令,不应该凭感觉,而应该看你的实际需求:做外部跳转用 return,做内部重写用 rewrite,需要精细控制参数合并方式时,用 rewrite 配合 $is_args$args。
把这个差异搞清楚,能让你少走很多弯路。
相关推荐:
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论