0

Nginx rewrite和return重定向区别详解:什么场景用哪个、怎么配才正确

2026.05.28 | youres | 8次围观

在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,逻辑更清晰

总结对比表

对比项returnrewrite
执行阶段早期重写阶段
参数保留默认不保留,需手动拼接默认保留,问号后内容会清空
正则支持不支持支持正则和捕获组
性能更轻量略重(可能多次匹配)
适用场景简单跳转、条件判断批量URL、模式匹配
last/break不支持支持

一句话建议:能用return解决的就用return,逻辑简单、性能更好;需要正则匹配批量URL时才上rewrite。这样分工,两个指令各司其职,重定向配置就不容易出问题了。

相关推荐

版权声明

本文仅代表个人观点。
本文系AI辅助作者原创,未经许可,转载请保留原文链接。

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