掌握Nginx Rewrite需理解“匹配—替换—执行”三层逻辑,依托PCRE正则(如^、$、()、\d等),严格遵循rewrite regex replacement [flag]语法,善用permanent/redirect/break/last标志,并优先选用return、location+proxy_pass等更优替代方案。

掌握 Nginx Rewrite 规则,关键不在堆砌语法,而在于理解“匹配—替换—执行”三层逻辑,并结合正则精准控制跳转行为。它不是万能跳转开关,而是 URI 处理流水线上的精密阀门。
正则表达式:Rewrite 的底层语言
所有 Rewrite 规则都依赖 PCRE(Perl 兼容正则)匹配请求 URI。必须掌握几个核心符号:
-
^ 表示 URI 起始位置,$ 表示结束位置,合起来
^/old$只匹配精确的/old,不匹配/old/或/old-page -
( ) 用于捕获分组,括号内匹配的内容可通过
$1、$2引用。例如^/user/(\d+)/profile$中的\d+匹配数字,重写为/api/v1/users/$1就能透传 ID - . 匹配任意单字符(除换行),.* 表示任意长度任意字符(常用但需谨慎),.+ 表示至少一个字符
- \d 匹配数字,[a-z] 匹配小写字母,[^/]+ 表示“非斜杠的连续字符”,适合提取路径段
rewrite 指令:三要素缺一不可
语法是 rewrite regex replacement [flag];,三个部分各自承担明确职责:
-
regex:必须是能被 PCRE 解析的模式,不能写成普通字符串(如直接写
/old不加^和$,会误匹配所有含/old的路径) -
replacement:可为相对路径(如
/new$1)、绝对路径(/app/index.html),或完整 URL(以http://、https://或$scheme://开头);一旦是完整 URL,Nginx 自动触发 301/302 跳转,无需显式 flag -
flag:决定后续流程走向,最常用四个:
•permanent→ 返回 301,搜索引擎收录新地址,适用于域名迁移、结构固化
•redirect→ 返回 302,浏览器跳转但不更新书签,适合灰度、维护页
•break→ 终止当前 location 内所有 rewrite,继续执行本 location 剩余配置(如 proxy_pass)
•last→ 终止当前 rewrite 链,用新 URI 重新发起 location 匹配(相当于内部重试)
实战中容易踩坑的典型场景
很多问题源于规则位置与 flag 搭配不当:
-
在 server 块顶部写跳转,别用 if:比如强制 HTTPS,直接用
return 301 https://$host$request_uri;,比if ($scheme = http) { rewrite ... permanent; }更高效、更安全(if 在 location 外使用有隐式限制) -
location 内 rewrite 优先选 break,不是 last:例如
location /img/ { rewrite ^/img/(.*)$ /static/$1 break; root /var/www; }—— break 确保重写后仍走本 location 的 root 查找,last 则会跳出再匹配,可能进错 location -
避免无限循环:若 rewrite 后 URI 仍匹配原规则(如
rewrite ^/(.*)$ /$1/;),Nginx 会在第 10 次重试后返回 500。应确保 replacement 改变 URI 结构,或用^/no-slash/(.*)$这类带锚点的精确模式 -
参数保留要靠 $request_uri:想把
/old?id=123跳到/new?id=123,不能只写rewrite ^/old$ /new permanent;(丢参数),得用rewrite ^/old$ /new?$request_uri permanent;或更稳妥的return 301 /new?$args;
替代方案:比 rewrite 更简洁的写法
不是所有跳转都需要 rewrite。简单定向优先考虑更轻量、更明确的指令:
-
return 指令:语义清晰、性能更高。例如
return 301 https://$host$request_uri;或return 404;,无正则开销,无匹配歧义 -
location + proxy_pass:需要内部转发而非暴露新地址时,用
location /api/ { proxy_pass http://backend/; },比 rewrite + proxy_pass 更直观 - map 指令:处理大量静态映射(如多域名归一、路径别名表),比一堆 if + rewrite 更易读、更高效


















