nginx地址重定向和重写
简单来说,重定向是告诉客户端“你要的资源在另一个地方,请去那里找”,而重写是服务器内部“悄悄地”把请求换个样子来处理,客户端对此一无所知。
下面我们详细解释并举例说明。
核心区别
| 特性 | 重定向 (Redirect) | 重写 (Rewrite) |
|---|---|---|
| 本质 | 客户端行为 | 服务器内部行为 |
| HTTP响应 | 返回 3xx 状态码(如301, 302) | 不返回3xx状态码(除非显式指定) |
| 浏览器地址栏 | 会变化为新的URL | 不会变化(因为是内部处理) |
| 请求次数 | 至少2次(客户端收到重定向指令后,会发起新请求) | 1次(服务器内部处理,对客户端透明) |
| 性能 | 稍差,因为多了额外的网络往返 | 更好,减少了客户端往返 |
| 主要指令 | return, rewrite (带 redirect 或 permanent 标志) |
rewrite (不带重定向标志) |
| 常见场景 | 域名变更、强制HTTPS、URL标准化 | 美化URL、动态URL映射、条件性路由 |
一、重定向 (Redirect)
重定向是服务器向客户端发送一个特殊的响应(HTTP状态码 3xx),指示客户端重新向一个新的URL发起请求。
主要指令:return
return 指令非常高效,通常用于直接返回状态码和URL。
举例说明:
-
经典场景:强制所有HTTP请求跳转到HTTPS
server { listen 80; server_name example.com; # 使用return 301进行永久重定向 return 301 https://$server_name$request_uri; }-
过程:用户访问
http://example.com-> Nginx返回301 Moved Permanently和Location: https://example.com-> 浏览器自动跳转到https://example.com。 -
效果:地址栏变化。
-
-
域名重定向:将旧域名跳转到新域名
server { listen 80; server_name old-site.com; # 将旧域名的所有请求重定向到新域名的对应页面 return 301 https://new-site.com$request_uri; }-
过程:访问
http://old-site.com/about.html-> 浏览器跳转到https://new-site.com/about.html。 -
效果:地址栏变化。
-
二、重写 (Rewrite)
重写是服务器在内部修改接收到的请求的URI,然后根据修改后的URI继续处理(如交给其他location处理或返回内容),客户端浏览器感知不到这个变化。
主要指令:rewrite
rewrite 指令的语法是:rewrite regex replacement [flag];
常用 flag:
-
last:用replacement重写后,跳出当前规则,重新用新的URI在server上下文中匹配location。 -
break:用replacement重写后,停止后续的rewrite规则,并在当前location中继续处理。 -
permanent:这不是内部重写,而是会返回 301 永久重定向给客户端。这实际上属于上面“重定向”的范畴。
举例说明:
-
美化URL(前端控制器模式)
这是最常见的作用。将动态的、复杂的URL重写为简洁美观的格式。-
真实URL:
/index.php?article=123 -
美化后想让用户看到的URL:
/articles/123
server { root /var/www/html; location / { # 尝试直接访问文件或目录,如果不存在... try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { # ... PHP-FPM配置 ... } # 重写规则:将 /articles/123 内部映射为 /index.php?article=123 rewrite ^/articles/(\d+)$ /index.php?article=$1 last; }-
过程:用户请求
/articles/123-> Nginx内部将其重写为/index.php?article=123-> 继续匹配location ~ \.php$来处理这个请求 -> 返回结果给用户。 -
效果:用户浏览器地址栏始终保持
/articles/123,不知道后台其实是index.php在干活。
-
-
条件性重写(if判断)
# 如果请求的文件不存在,则重写到一個默认图片 if (!-f $request_filename) { rewrite ^/images/(.*)$ /images/default.png break; }-
过程:请求
/images/photo.jpg(如果该文件不存在)-> Nginx内部将请求修改为/images/default.png-> 返回默认图片。 -
效果:用户浏览器地址栏仍然是
/images/photo.jpg,但看到的是default.png的内容。
-
三、rewrite 指令的双重角色
需要注意的是,rewrite 指令根据使用的 flag 不同,可以扮演两种角色:
-
内部重写(服务器行为):当使用
last或break时。 -
外部重定向(客户端行为):当使用
permanent或redirect时。
重定向示例(使用 rewrite):
# 临时重定向 (302)
rewrite ^/old-page$ /new-page redirect;
# 永久重定向 (301)
rewrite ^/old-page$ /new-page permanent;
这和使用 return 301 /new-page; 效果类似,但 return 指令更直接高效,通常作为重定向的首选。
总结
| 你的需求 | 应使用 | 示例 |
|---|---|---|
| 告诉用户/搜索引擎URL已永久变更 | 重定向 (return 301) |
return 301 https://$host$request_uri; |
| 临时将用户引导到另一个页面 | 重定向 (return 302 或 rewrite ... redirect) |
return 302 /maintenance.html; |
| 美化URL,隐藏真实后端脚本 | 重写 (rewrite ... last) |
rewrite ^/blog/(\d+)$ /index.php?id=$1 last; |
| 根据条件(如文件不存在)改变请求目标 | 重写 (rewrite ... break) |
if (!-f $request_filename) { rewrite ... break; } |
简单记法:看浏览器地址栏会不会变。会变的是重定向,不变的是重写。
四、rewrite 指令的flag区别
好的,我们来深入探讨Nginx rewrite 指令中几个核心 flag 的区别。理解这些标志是掌握Nginx重写规则的关键。
最常用的四个标志是:last, break, permanent, redirect。
它们可以分为两大类:
-
内部处理类 (
last,break):用于服务器内部重写URI,不会让客户端发起新请求。 -
客户端重定向类 (
permanent,redirect):会中断处理并向客户端返回一个重定向响应(3xx),会让客户端发起新请求。
内部处理类 Flag
这类Flag的特点是:浏览器地址栏不会改变,整个过程在服务器内部完成。
1. last
-
行为:停止当前轮的
rewrite模块指令,用重写后的新URI,重新发起一轮新的请求匹配。新的URI会重新匹配server块中的location。 -
过程比喻:就像在一个办公室里,A部门的员工(当前location)把你的申请单(请求URI)修改了一下,然后对你说:“这个不归我们管,你拿着这个新单子,重新去前台问一下该去哪个部门吧。” 于是你回到前台,前台根据新单子把你指引到B部门。
-
搜索范围:会重新搜索
server级别的所有location。 -
使用场景:非常通用,是大多数重写规则的首选,特别是当重写后的URI需要被另一个
location块处理时。
示例:
location /api/ {
rewrite ^/api/(.*)$ /app/index.php?route=$1 last;
}
location ~ \.php$ {
# 处理PHP请求的配置(FastCGI等)
...
}
流程:
-
请求
/api/users/1 -
rewrite规则匹配,URI被重写为/app/index.php?route=users/1 -
last标志触发,Nginx重新开始查找能处理/app/index.php?route=users/1的location -
新的URI匹配到
location ~ \.php$,请求被交给PHP解释器处理。
2. break
-
行为:停止当前轮的
rewrite模块指令,并且不再重新匹配location,直接在当前location中继续处理重写后的URI。 -
过程比喻:还是那个办公室,A部门的员工修改了你的申请单,然后说:“行了,我知道接下来该怎么处理了,你就在这等着吧。” 他不会再让你去前台重新问路。
-
搜索范围:不会重新搜索 location。只在当前上下文中继续。
-
使用场景:
-
在非
location块(如server块)中使用时,重写后继续正常处理(如尝试查找文件)。 -
在
location块中,当你确信重写后的URI就应该由当前这个location处理,或者只需要进行简单的文件路径映射时。
-
示例:
# 示例1:在server块中,将 /static/* 映射到 /data/assets/*
server {
...
rewrite ^/static/(.*)$ /data/assets/$1 break;
# 重写后,Nginx会尝试寻找文件 /data/assets/css/style.css
# 而不会重新匹配location
...
}
# 示例2:在location块中
location /images/ {
# 如果文件不存在,则提供默认图片
if (!-f $request_filename) {
rewrite ^/images/(.*)$ /images/default.jpg break;
}
# break后,会直接尝试寻找 /images/default.jpg 文件
}
关键区别:last vs breaklast 会发起新一轮location匹配,而 break 不会。如果上面的 last 示例错用了 break:
location /api/ {
rewrite ^/api/(.*)$ /app/index.php?route=$1 break; # 错误地使用了break
}
location ~ \.php$ {
...
}
流程会变成:
-
请求
/api/users/1 -
URI被重写为
/app/index.php?route=users/1 -
break标志触发,Nginx停止重写,并尝试在当前location /api/中处理/app/index.php?route=users/1这个请求。 -
由于
/api/这个location通常不配置处理PHP,最终很可能会返回404错误或直接下载PHP文件。
客户端重定向类 Flag
这类Flag的特点是:会中断当前请求,并向客户端返回一个重定向响应(3xx状态码),浏览器地址栏会变为新的URL。
3. permanent
-
行为:返回 301 Moved Permanently(永久重定向)状态码给客户端。
-
影响:搜索引擎会记住这个重定向,将权重和排名转移到新的URL上。
-
使用场景:域名永久迁移、强制HTTPS、URL结构永久变更。这是SEO友好的做法。
示例:
# 永久将旧域名重定向到新域名
server {
listen 80;
server_name old.com www.old.com;
return 301 https://new.com$request_uri; # 使用return更标准
# 或者用 rewrite
# rewrite ^(.*)$ https://new.com$1 permanent;
}
4. redirect
-
行为:返回 302 Found(临时重定向)状态码给客户端。
-
影响:搜索引擎不会记住这个重定向,认为这只是临时的。
-
使用场景:临时维护页面、A/B测试等需要临时跳转的情况。
示例:
# 网站临时维护,跳转到维护页面
location / {
rewrite ^(.*)$ /maintenance.html redirect;
}
总结与选择指南
| Flag | 类型 | 行为 | 浏览器地址栏变化? | 常用场景 |
|---|---|---|---|---|
last |
内部重写 | 停止当前重写,重新匹配location | 否 | 通用重写,特别是需要PHP/代理处理的美化URL |
break |
内部重写 | 停止当前重写,在当前上下文继续 | 否 | 静态文件路径映射、在当前location内完成处理 |
permanent |
客户端重定向 | 返回 301 永久重定向 | 是 | 永久性更改(域名、HTTPS、旧链接) |
redirect |
客户端重定向 | 返回 302 临时重定向 | 是 | 临时跳转(维护、测试) |
简单选择原则:
-
你想让用户浏览器地址栏变吗?
-
想变 -> 用
permanent(永久) 或redirect(临时)。优先考虑更高效的return 301/302指令。 -
不想变 -> 用
last或break。
-
-
用
last还是break?-
重写后的URI需要由另一个
location(如PHP处理块、代理转发块)来处理 -> 用last。 -
重写后的URI就在当前上下文处理(如找另一个文件)-> 用
break。在server块顶层也常用break。
-

浙公网安备 33010602011771号