在配置Nginx跳转时,return和rewrite是两个最容易混淆的指令。不少人遇到过这样的问题:明明配置了rewrite,页面却出现了循环重定向;或者用return做了跳转,却发现某些请求根本没被匹配到。要彻底搞清楚这两个指令的区别,需要从它们在Nginx处理请求流程中的位置说起。本文将从执行机制、语法细节、典型场景和常见坑点几个方面展开分析。

一、执行机制:return是直接响应,rewrite是重写引擎
return指令属于Nginx的指令级动作,它在被匹配到的瞬间就直接向客户端返回响应,不再执行同一上下文中后续的其他指令。也就是说,一旦return被执行,当前请求的处理流程基本就结束了。这带来两个特点:一是执行效率高,没有多余的正则匹配开销;二是行为简单直接,不容易产生意料之外的副作用。
rewrite则完全不同,它工作在Nginx的rewrite阶段,依赖PCRE正则引擎对URI进行匹配和替换。rewrite指令本身并不一定产生跳转,它的本职工作是重写URI。只有当重写后的URI需要跨server块重新处理,或者配合特定的flag标志位时,才会真正发起外部跳转。例如常见的last标志会让重写后的URI重新走一遍location匹配,而break则停留在当前location继续处理。redirect和permanent两个标志分别对应302和301外部重定向。
可以这么理解:return是“直接给答案”,rewrite是“改完题目再看要不要重新作答”。这个本质差异决定了它们在配置风格和适用场景上的不同。
二、语法与标志位详解
return的语法非常简洁,支持返回状态码加URL,也支持返回状态码加文本内容:
# 直接返回状态码 return 403; # 301永久重定向到新地址 return 301 https://example.ipipp.com$request_uri; # 返回302临时重定向 return 302 /maintenance.html; # 直接返回文本 return 200 "ok";
rewrite的语法则包含三部分:正则表达式、替换内容和可选的flag标志位:
server {
listen 80;
server_name old.ipipp.com;
# permanent表示301永久跳转
rewrite ^/(.*)$ https://new.ipipp.com/$1 permanent;
# redirect表示302临时跳转
rewrite ^/news-(\d+)\.html$ /news/$1.html redirect;
# last:重新进行location匹配
rewrite ^/api/v1/(.*)$ /api/$1 last;
# break:停止后续rewrite,在当前location继续处理
rewrite ^/static/(.*)$ /cdn/$1 break;
}
四个常用flag的含义需要牢记:last重写后重新搜索location并匹配;break停止rewrite处理但留在当前location;redirect返回302;permanent返回301。容易出错的是last和break的区别,last会触发新一轮的location匹配,如果新URI仍然命中同一location内的rewrite规则,就可能形成循环,Nginx默认会在十次循环后报500错误。break则不会重新匹配,相对安全。
三、典型场景对比与选型建议
场景一:强制HTTP跳转HTTPS。这是return最经典的使用场景,因为跳转逻辑简单,只需要拼上原请求URI即可:
server {
listen 80;
server_name ipipp.com www.ipipp.com;
return 301 https://$host$request_uri;
}
如果这里改用rewrite写法,虽然也能实现,但要写正则捕获,代码更冗长,还多了一次正则匹配的性能开销,完全没有必要。Nginx官方文档也明确建议:能用return完成的跳转就不要用rewrite。
场景二:域名整体迁移。整站从旧域名换到新域名,同样适合return,直接在server级别处理所有请求:
server {
listen 80;
listen 443 ssl;
server_name old-domain.ipipp.com;
return 301 https://www.ipipp.com$request_uri;
}
场景三:带正则规则的URL转换。比如把带参数风格的旧URL迁移到新的路径风格,这时URI之间不是简单的等价替换,需要正则捕获和拼接,return就力不从心了,应该用rewrite:
# 旧URL /article-123.html 跳转到 /article/id/123
location / {
rewrite ^/article-(\d+)\.html$ /article/id/$1 permanent;
}
场景四:内部重写而不跳转。这是rewrite独有的能力,return做不到。比如把外部URI重写成内部路径再交给后端FastCGI处理,典型如WordPress、Typecho等程序的伪静态规则,全部依赖rewrite的last或break完成,浏览器地址栏不会发生变化。
四、常见坑点与注意事项
第一个坑是在server块中混用return和rewrite导致rewrite失效。由于return会立即终止处理,写在return之后的rewrite永远不会被执行。排查这类问题时要检查指令的书写顺序。
第二个坑是rewrite与location的嵌套循环。在location内部使用带last标志的rewrite时,如果重写结果又命中同一个location,就会反复重写直到触发循环上限。解决办法是调整正则让重写后的URI不再命中,或改用break标志。
第三个坑是HTTP与HTTPS的SNI问题。在同一个server块中监听80和443时,如果证书配置有问题,return 301的写法可能在某些客户端上引发告警。更稳妥的做法是按官方建议把80端口的server块单独拎出来只做跳转,443的server块专注处理业务。
第四个坑是rewrite中的正则转义。点号、问号等字符在正则中有特殊含义,匹配时记得转义,比如匹配.html要写成\.html。另外rewrite的替换部分如果包含请求参数,原查询字符串会自动追加,除非在替换内容末尾加一个问号显式截断。
总结一下选型原则:只是简单的整站跳转、域名迁移、强制HTTPS,用return加状态码,简洁高效;需要正则捕获、URI结构转换或内部重写伪静态,用rewrite。理解了return是直接响应、rewrite是重写引擎这个本质区别,绝大多数配置问题都能迎刃而解。
Nginx returnNginx rewriteURL跳转修改时间:2026-09-04 12:18:34