nginx跳转配置的核心指令只有两个:return和rewrite。前者适合明确的一对一跳转,后者适合需要正则匹配或条件判断的场景。两者都来自ngx_http_rewrite_module模块,但很多配置文件中出现的错误,并不是语法写错,而是执行顺序理解错。nginx在server层级和location层级都会处理rewrite指令,同一层级中return的执行优先于所有rewrite;一旦return执行,后续的重写规则全部停止。因此,如果在一个location里先写了return,再写若干rewrite,那些rewrite永远不会被执行。这个执行顺序是排查跳转失效问题的第一把钥匙。

另外还要理解location的匹配阶段。nginx先根据请求URI匹配location,再执行对应location中的rewrite规则。精确匹配、前缀匹配、正则匹配的优先级不同,如果旧路径对应的location没有命中,跳转规则写在哪一层都不会生效。通常建议:简单路径跳转使用location = /old精确匹配;批量路径跳转使用location ~ ^/old/正则匹配;整站跳转放在server层级,直接return 301即可。
一、return指令的用法与优先级
return指令的基本语法是return code URL;。其中code可以是301、302、303、307、308等HTTP状态码。301表示永久重定向,搜索引擎会把收录权重转移到新地址,适合已经确定不再变化的旧链接。302表示临时跳转,浏览器不会更新收藏,搜索引擎也不会立即替换收录内容。如果只是活动页面临时换地址,不应该用301,而应该用302,避免错误传递永久迁移信号。
在同一个location中,return一旦执行,请求处理立即结束,nginx不会再执行该location内后面的rewrite或其他rewrite模块指令。这意味着配置顺序非常重要。例如下面这个错误示例中,rewrite永远不会生效:
location /demo/ {
return 301 /new-demo/;
# 下面的规则不会被执行
rewrite ^/demo/(.*)$ /new-demo/$1 permanent;
}
正确做法是:如果目标地址是固定的,直接使用return 301;如果目标地址需要从URI中提取动态部分,则不要提前写return,改用rewrite配合permanent参数。或者把return放在if条件块中,仅当条件满足时才提前返回。另一个常见问题是return 301 /new-page;这种写法会丢失原始查询参数。如果需要保留查询参数,应该使用$request_uri或显式拼接$args。很多开发者在整站迁移时只写了路径,结果旧链接中的搜索参数全部丢失,导致落地页统计错乱。
return还支持不带URL的用法,例如return 301;这种写法会返回一个空Location头,实际场景中很少使用。为了让跳转语义更明确,建议始终携带完整目标URL,并且优先使用绝对URL,例如https://www.ipipp.com/new-page,而不是相对路径。使用相对路径虽然允许,但不同版本或反向代理场景下可能产生歧义,不利于排查。
二、rewrite指令与301永久重定向
rewrite指令的语法是rewrite regex replacement [flag];。其中permanent标志等同于301永久重定向,redirect等同于302临时跳转。与return相比,rewrite的优势在于可以使用正则表达式捕获原URI中的动态部分,并把这些部分拼接到新地址中。例如把旧的文章路径/article/123.html迁移到新的查询参数形式/post?id=123,用return很难做到,但用rewrite非常直观:
location ~ ^/article/([0-9]+)\.html$ {
rewrite ^/article/([0-9]+)\.html$ /post?id=$1 permanent;
}
这里$1表示正则捕获到的一组数字。注意location本身已经写了正则,rewrite内部又写了一遍正则,这是为了同时完成匹配和替换。如果觉得重复,可以改用if加return,但nginx官方不推荐过度使用if,因为if在nginx中的行为并不像编程语言那样直观。对于路径格式固定的迁移,建议尽量用位置匹配加return;只有在确实需要动态捕获时,再使用rewrite。
rewrite与return的另一个差异是执行效率。return直接结束处理并返回响应,rewrite则会重新进入匹配流程,nginx会重新查找新的location。如果一次请求经历多次rewrite,会增加CPU开销,并可能造成循环跳转。因此nginx官方建议优先使用return,特别是在server层级做整站跳转时,直接写:
server {
listen 80;
server_name old.ipipp.com;
return 301 https://www.ipipp.com$request_uri;
}
这个配置简单、清晰、性能最好。$request_uri包含原始URI以及查询参数,不会丢失?q=nginx这类内容。相比$uri,$request_uri不会被解码或规范化,更适合用于跳转原样保留。如果使用rewrite ^ $scheme://www.ipipp.com$request_uri permanent;也能实现类似效果,但写法和维护成本都不如return。
三、典型场景与完整配置示例
场景一:域名合并。很多网站同时持有ipipp.com和www.ipipp.com,搜索引擎会把它们当作两个站点,导致权重分散。通常做法是选择一个作为主域名,另一个301跳转过去。下面示例将www跳转到非www,同时兼容旧域名:
server {
listen 80;
server_name www.ipipp.com;
return 301 http://ipipp.com$request_uri;
}
server {
listen 80;
server_name old-site.com www.old-site.com;
return 301 https://www.ipipp.com$request_uri;
}
场景二:HTTP到HTTPS升级。证书配置完成后,需要在80端口上把流量跳到443端口。下面是一个常用的安全写法,default_server可以接收没有匹配到其他server_name的请求:
server {
listen 80 default_server;
server_name _;
return 301 https://$host$request_uri;
}
这里使用$host而不是直接写死域名,可以适配多域名站点。但要注意,如果用户请求中带有异常Host头,$host可能会被带入跳转地址,造成开放重定向风险。对于安全要求较高的站点,建议明确列出允许的域名。如果架构中存在反向代理,HTTPS在代理层终止,源站的80端口请求可能带有X-Forwarded-Proto头,此时应根据该头判断而不是直接跳转。下面是一种常见写法:
server {
listen 80;
server_name ipipp.com;
if ($http_x_forwarded_proto = "http") {
return 301 https://$host$request_uri;
}
}
场景三:路径迁移。旧栏目/legacy/整体迁到/new/,需要把URI中后面的部分原样保留。用rewrite permanent可以完成:
location /legacy/ {
rewrite ^/legacy/(.*)$ /new/$1 permanent;
}
如果只想迁移某一个固定页面,精确匹配是更好的选择:
location = /old-page {
return 301 /new-page;
}
需要保留查询参数时,可以在目标地址中追加$request_uri,但不要重复拼接。例如return 301 /new-page$request_uri;会把整个原始URI包括路径也带过去,这通常不是想要的。正确的做法是:目标路径固定时,用$query_string或者$args单独拼接;目标路径由变量决定时,用$request_uri表示原样保留。例如:
location = /old-page {
return 301 /new-page?$args;
}
上面的配置可以保留原始查询参数,同时路径改成新地址。但要注意,如果原始请求没有查询参数,$args为空,最终URL末尾会多一个?,这是无害的,但如果介意可以结合if判断,不过增加复杂度通常没有必要。
四、常见问题与注意事项
第一个要警惕的是循环重定向。当两个规则互相跳转时,浏览器会提示重定向次数过多。例如用户在ipipp.com和www.ipipp.com之间来回跳,或者HTTP跳HTTPS后又从HTTPS跳回HTTP。排查方法是先查看响应头中的Location,确认目标地址是否符合预期,再检查server_name和location的匹配范围是否互相覆盖。使用curl -I可以只发HEAD请求查看状态码和Location:
curl -I http://ipipp.com/old-page
第二个问题是POST请求经过301后可能变成GET。这是HTTP标准行为,301、302在历史实现中会把POST改为GET。如果接口迁移后必须保留POST方法,应该使用307或308状态码。307是临时跳转并保留方法,308是永久跳转并保留方法。nginx的return和rewrite都支持这些状态码,但需要浏览器和客户端的兼容性。对于大部分网页页面跳转,301没有问题;对于API接口,建议先测试客户端是否支持308。
第三个问题是端口丢失。当nginx监听非标准端口时,使用$host或server_name拼接跳转地址,可能会漏掉原始端口。例如用户在http://ipipp.com:8080访问,跳转后变成https://ipipp.com,如果443端口服务不同,就会出错。需要保留端口时,可以用$http_host替代$host,因为$http_host会带上原始请求中的端口号。但$http_host来自客户端头,可能被伪造,安全场景需要过滤。正式生产环境建议显式写完整域名和端口。
第四个问题是location匹配优先级。nginx的匹配顺序是先精确匹配,再前缀匹配,最后正则匹配。正则匹配按照配置文件中出现顺序执行,一旦匹配到正则location,就不会再执行后续的正则location。因此,如果旧路径的正则规则写在一大堆正则后面,可能永远轮不到它执行。建议把跳转规则尽量放到server层级或精确location中,减少受匹配顺序影响的风险。每修改一次配置后,使用nginx -t检查语法,再平滑加载nginx -s reload,不要直接重启服务,避免短暂中断流量。
第五个问题是测试跳转时只看浏览器地址栏变化,忽略了中间跳转链。一次跳转可能经过多个301,每次都会增加延迟,而且搜索引擎也可能只追踪有限次跳转。可以用curl -I -L查看完整跳转链,确认是否最短路径。对于已经确认的旧地址,应该一次性跳到最终地址,而不是先跳到一个中间地址再跳第二次。
五、配套练习题推荐
第一题:现有域名a.ipipp.com需要永久迁到b.ipipp.com,要求保留完整URI和查询参数,请写出server层级配置。第二题:旧文章路径/news/2020/01/01/abc.html需要跳转到/article/abc,如何用正则捕获abc部分。第三题:配置一个location,当访问/old时301到/new,但/old?page=2需要跳转到/new?page=2,请写出两种实现方式并说明哪种更优。第四题:如果用户在HTTP和HTTPS之间出现循环跳转,应该检查哪些配置项?第五题:说明301与302在搜索引擎收录上的区别,以及为什么接口场景可能需要308。
这些练习题可以手动在测试环境验证。完成后再对照上文示例检查,尤其是是否保留了查询参数、是否避免了正则重复、是否误用了rewrite。nginx跳转配置最终的目标不是让页面打开,而是让搜索引擎、浏览器和接口客户端都用最低成本到达正确地址,同时保持URI语义完整。理解了return与rewrite的执行顺序和适用边界,大部分跳转需求都能用几行配置稳定解决。
nginx跳转配置nginx301重定向重定向规则修改时间:2026-09-22 10:02:46