Nginx rewrite模块是实现URL重写和跳转的核心功能,无论是域名迁移、HTTP强制跳转HTTPS,还是给动态页面做伪静态,都离不开它。不过rewrite的语法看似简单,背后却涉及location匹配顺序、flag标志位、正则捕获组等一系列细节,稍有疏忽就会出现死循环、跳转失效或者性能下降的问题。本文结合具体实例,把rewrite的用法和常见坑讲清楚。

一、rewrite指令的基本语法与执行顺序
rewrite的完整语法是rewrite regex replacement [flag];,其中regex是要匹配的URI正则表达式,replacement是替换后的目标地址,flag是可选的标志位。这条指令只能写在server、location或者if块中,作用对象是请求的URI部分,不包含主机名和查询参数。
一个典型的例子:
server {
listen 80;
server_name ippipp.com;
# 把 /old-page.html 重写为 /new-page.html
rewrite ^/old-page\.html$ /new-page.html last;
# 把所有 /article/数字 的请求重写为动态脚本
rewrite ^/article/(\d+)$ /article.php?id=$1 last;
}
这里要注意几个细节。正则中的^和$分别锚定URI的开头和结尾,点号.必须转义成\.才能匹配字面意义上的点,否则会匹配任意字符。$1表示正则中第一个圆括号捕获的内容,如果有多个括号,依次用$1、$2引用。如果replacement中包含新的查询参数,原有的查询串默认会自动拼接在后面,若想丢弃原参数,可以在目标地址末尾加一个?。
执行顺序也是必须理解的一点。Nginx收到请求后,先在server块内按顺序执行rewrite指令,然后确定命中的location,再在该location内执行rewrite。如果重写后的URI发生了变化,Nginx会重新去匹配location,这就是很多死循环问题的根源。
二、四种flag的区别与实例演示
flag决定了重写之后Nginx的后续行为,一共有四种:last、break、redirect、permanent,用错它们是初学者最常见的问题。
- last:停止当前rewrite指令集的执行,用重写后的URI重新搜索location,适合内部重写。
- break:停止rewrite指令的执行,但不再重新匹配location,直接在当前location内处理请求,常用于静态资源目录。
- redirect:返回302临时重定向,浏览器地址栏会变化,适合临时调整。
- permanent:返回301永久重定向,搜索引擎会把权重转移到新地址,适合域名迁移。
看一下last和break的实际差异:
location /download/ {
# break后不再重新匹配location,直接按当前上下文处理
rewrite ^/download/(.*)$ /data/files/$1 break;
# 下方location内的fastcgi等配置仍然生效
}
location /api/ {
# last会重新匹配location,重写后的URI会命中其他location
rewrite ^/api/v1/(.*)$ /api/index.php?route=$1 last;
}
如果把/download/的break换成last,重写后的/data/files/xxx会重新去匹配location,可能命中别的处理逻辑,导致文件找不到。而/api/的场景恰恰需要重新匹配,让新的URI走PHP处理。判断标准很简单:重写后需要交给其他location处理就用last,重写后就在当前location内消化就用break。
301永久重定向的典型场景是老域名迁移:
server {
listen 80;
server_name old-domain.com;
# 整站迁移到新域名,保留原有路径
rewrite ^/(.*)$ http://new-domain.com/$1 permanent;
}
三、常见实战案例
1. HTTP强制跳转HTTPS
这是线上最常见的需求,推荐用return而不是rewrite,效率更高:
server {
listen 80;
server_name ippipp.com;
return 301 https://$host$request_uri;
}
2. 去掉URL末尾的斜杠
统一/dir/和/dir两种形式有利于SEO:
# 把 /foo/ 重写为 /foo
if ($request_uri ~ ^/(.*)/$) {
return 301 /$1;
}
3. 多级目录伪静态
把/goods/12/34.html映射为/goods.php?cate=12&id=34:
location / {
rewrite ^/goods/(\d+)/(\d+)\.html$ /goods.php?cate=$1&id=$2 last;
}
4. 根据User Agent做跳转
移动端访问跳转到wap站点:
if ($http_user_agent ~* "(iphone|android)") {
rewrite ^/$ https://m.ipipp.com/ redirect;
}
四、生产环境避坑要点
第一,警惕死循环。比如写了rewrite ^/(.*)$ /index.php/$1 last;,而/index.php所在的location又包含这条rewrite规则,URI会无限重写。Nginx虽然内置了十次循环上限并返回500错误,但这说明配置本身有逻辑问题。解决办法是合理划分location,或者改用break。
第二,能用return就不要用rewrite。对于固定的跳转,return 301 https://$host$request_uri;比rewrite更简洁高效,return直接结束处理,省去了正则匹配的开销。rewrite应该留给确实需要正则捕获的场景。
第三,注意if的陷阱。rewrite模块的if指令功能有限,只适合做字符串和正则比较,不要在if里写嵌套逻辑或者尝试set以外的内容操作。官方文档也明确说过if is evil,能用map和location替代就尽量替代。
第四,修改配置后一定要用nginx -t检查语法,再用nginx -s reload平滑加载。同时建议在测试环境用curl的-I参数验证返回码和Location头是否符合预期,确认无误后再推到线上,避免301这种影响面大的变更出问题。
掌握这些原则后,rewrite基本能覆盖日常绝大多数URL处理需求。核心还是理解location匹配机制与flag的行为差异,写规则前先画出请求流转路径,很多问题就能提前规避。
Nginx rewrite重写规则Nginx配置修改时间:2026-09-08 19:18:54