在Nginx里做重定向,rewrite和return是最常用的两个指令。很多人都知道它们能跳转URL,但具体区别在哪、什么时候该用哪个,往往搞不清楚。用错了,轻则查询参数莫名消失,重则整站陷入重定向循环。这篇文章从原理出发,把两者的核心差异讲清楚,再给出实战配置示例。
先说结论:它们根本不是一回事
表面上看,rewrite和return都能把请求从一个地址跳到另一个地址。但从Nginx内部处理流程来看,它们是两套完全不同的机制。
- return 是Nginx的内置指令,执行到它时直接终止当前请求处理阶段,跳到下一个阶段完成响应或跳转。
- rewrite 是模块指令,会修改内部请求URI,然后重新进入请求处理流程,可能再次匹配location。
这个区别听起来抽象,但它直接决定了三个实际问题:重定向是否保留参数、是否会触发内部重定向、以及性能开销有多大。
return指令的核心特性
直接响应,不走回头路
当Nginx执行到return指令时,处理逻辑非常简单:停止当前location的处理,把控制权交给下一个阶段。
server {
listen 80;
server_name example.com;
# 直接返回301,终止处理
return 301 https://example.com;
# 这行配置永远不会被执行
rewrite ^/(.*)$ https://example.com/ permanent;
}
return后面的指令不会被执行,这是它的优点——逻辑清晰,不容易出幺蛾子。
return保留参数吗?
这是最容易踩坑的地方。直接写 return 301 https://example.com/new; 会丢失原始URL的查询参数。如果要保留参数,必须手动拼接:
# 不保留参数
return 301 https://example.com/new;
# 保留所有查询参数
return 301 https://example.com/new;
# 示例:原URL是 https://example.com/old?utm_source=baidu
# 重定向到 https://example.com/new?utm_source=baidu
很多人遇到"重定向后UTM参数消失"的问题,根源就在这里——用了简写形式,没有加 。
return支持的几种写法
# 返回状态码
return 301; # 永久重定向
return 302; # 临时重定向
return 444; # 关闭连接(不返回响应头)
# 返回文本内容
return 200 "Service is under maintenance";
# 返回JSON
return 200 '{"code":503,"msg":"Service Unavailable"}';
# 重定向到固定URL
return 301 https://example.com/newpage;
# 重定向到动态URL(自动保留参数)
return 301 https://example.com/newpage;
rewrite指令的核心特性
修改URI,重新走一遍匹配流程
rewrite的工作方式不同——它修改了请求的URI,然后Nginx会用新URI重新走一遍location匹配流程。这意味着rewrite可能触发内部重定向。
server {
listen 80;
server_name example.com;
# 匹配 /old-page 的请求,rewrite后重新匹配
rewrite ^/old-page /new-page permanent;
# 如果匹配到上面rewrite后的结果,会进入这个location
location /new-page {
return 200 "reached via rewrite";
}
}
rewrite的参数处理机制
rewrite在重定向时,问号(?)的行为比较特殊:
# rewrite规则中的?会清空原查询参数
rewrite ^/old /new? permanent;
# 原URL: /old?utm_source=baidu
# 结果: /new(参数被清空)
# 不带?,则保留原查询参数
rewrite ^/old /new permanent;
# 原URL: /old?utm_source=baidu
# 结果: /new?utm_source=baidu(参数保留)
这个行为和return恰恰相反。return默认清空参数,rewrite默认保留参数(除非加了显式问号)。
rewrite的flag参数
rewrite支持四个flag,每个决定了不同的处理方式:
- last:用重写后的URI重新匹配location,不修改浏览器地址栏
- break:停止rewrite处理,在当前location内继续执行后续指令
- redirect:返回302临时重定向
- permanent:返回301永久重定向
# last示例:内部重定向,不改变浏览器URL
rewrite ^/article/(\d+)$ /article?id= last;
# break示例:rewrite后不重新匹配location
rewrite ^/static/(.*)$ /assets/ break;
# redirect示例:302临时重定向
rewrite ^/old-product /new-product redirect;
# permanent示例:301永久重定向
rewrite ^/old-blog /new-blog permanent;
两者的性能差异
从性能角度,return比rewrite更轻量:
- return是Nginx核心指令,在处理流程的早期阶段就执行完毕
- rewrite需要经过重写模块处理,可能触发多次location匹配
在高频重定向场景下(每天百万次级别),这个差异可能达到10%~20%的CPU开销差距。不过对于普通站点来说,基本感觉不到影响。
实战场景:到底该用哪个?
场景1:简单固定URL跳转 → 用return
# HTTP跳转到HTTPS,推荐用return
server {
listen 80;
server_name example.com;
return 301 https://example.com;
}
# 简单旧页面到新页面跳转
location = /discontinued {
return 301 /products;
}
场景2:需要正则匹配多个URL → 用rewrite
# 把所有旧文章URL重定向到新结构
rewrite ^/blog/(\d{4})/(\d{2})/(.*)$ /articles/ permanent;
# 批量处理多个旧路径
if ( ~ ^/old-(.*)$) {
rewrite ^/old-(.*)$ /new- permanent;
}
场景3:SPA单页应用的路由处理 → 用rewrite + break
location / {
try_files / /index.html;
rewrite ^ /index.html break;
}
场景4:带条件判断的重定向 → 两者结合
# 根据User-Agent判断移动端跳转
if ( ~* "Mobile|Android|iPhone") {
return 301 https://m.example.com;
}
# 根据来源域名判断跳转
if ( ~* "old-domain.com") {
rewrite ^ http://new-domain.com permanent;
}
常见踩坑点汇总
- return重定向丢失参数 → 记得加
- rewrite死循环 → 检查last/break flag是否用对,location匹配顺序是否正确
- rewrite和return同时配置 → return优先级更高,后面的rewrite不执行
- 在if里用rewrite做外部重定向 → 建议改用return,逻辑更清晰
总结对比表
| 对比项 | return | rewrite |
|---|---|---|
| 执行阶段 | 早期 | 重写阶段 |
| 参数保留 | 默认不保留,需手动拼接 | 默认保留,问号后内容会清空 |
| 正则支持 | 不支持 | 支持正则和捕获组 |
| 性能 | 更轻量 | 略重(可能多次匹配) |
| 适用场景 | 简单跳转、条件判断 | 批量URL、模式匹配 |
| last/break | 不支持 | 支持 |
一句话建议:能用return解决的就用return,逻辑简单、性能更好;需要正则匹配批量URL时才上rewrite。这样分工,两个指令各司其职,重定向配置就不容易出问题了。
相关推荐
版权声明
本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

发表评论