0

Nginx rewrite正则表达式捕获组用法详解:$1到$9到底怎么用

2026.05.26 | youres | 10次围观

我们在配置 Nginx 时,经常会遇到 URL 重写的需求:比如把 /news/12345.html 重写到 /article/12345,或者根据请求来源做条件跳转。做到这些,离不开正则表达式捕获组。

这篇文章就来讲清楚:Nginx rewrite 里捕获组到底是什么、 怎么来的、什么时候用、哪些地方能用、哪些坑要避开。

一、先搞懂基本语法:rewrite 和捕获组的关系

Nginx 的 rewrite 指令长这样:

rewrite regex replacement [flag];

当 regex(正则表达式)和请求 URI 匹配上之后,URI 就会被 replacement 替换掉。

正则表达式里的圆括号 () 就是捕获组。每个括号里的内容会被"抓"出来,按顺序生成 ……最多到

举个例子:

rewrite ^/article/(\d+)/(\w+)\.html$ /article/- permanent;

如果用户访问 /article/12345/tutorial.html,匹配之后:

  • = 12345(第一个括号 (\d+)
  • = tutorial(第二个括号 (\w+)

最终替换结果:/article/12345-tutorial,返回 301 永久重定向。

二、if 指令中的捕获组:条件判断的利器

除了 rewrite,Nginx 的 if 指令也支持正则匹配,而且同样能捕获分组。这在根据请求特征做条件判断时特别有用。

提取 Cookie 中的值

if (\ ~* "id=([^;]+)(?:;|\$)") {
    set \ \;
}

这行配置的意思是:

  • 用正则 id=([^;]+) 从 Cookie 里提取 id= 后面的值(遇到分号为止)
  • 捕获到的内容赋给变量 \
  • 再用 set \ \\ 的值存到自定义变量 \

根据 User-Agent 做跳转

if (\ ~ MSIE) {
    rewrite ^(.*)\$ /msie/\ break;
}

这里只有一个捕获组 (.*),匹配到的完整 URI 存入 \,然后重写到 /msie/ 路径下。

三、replacement 字符串的细节

捕获组拿到之后,在 replacement 里怎么写有几条规则要注意:

1. 直接引用变量即可

在 replacement 里直接写 \\,Nginx 会自动替换成对应的捕获内容:

rewrite ^/old/(\d{4})/(\d{2})/(.*)\$ /new/\-\/\ last;

这个例子把 /old/2024/06/article 重写成 /new/2024-06/article,年月日格式重组就用到了多个捕获组。

2. 保留旧参数:加个问号结尾

默认情况下,replacement 之后原始的查询参数会追加到新 URL 后面。如果不想追加,在 replacement 最后加一个 ?

rewrite ^/users/(.*)\$ /show?user=\ last;

这里 \ 是用户名,末尾的 ? 表示"不要附加旧参数"。如果不加 ?,请求 /users/tom?page=3 会变成 /show?user=tom&page=3(有时会造成参数混乱)。

3. 以 http:// 开头则中断重写发跳转

如果 replacement 字符串以 http://https://\ 开头,Nginx 会直接返回 302 临时重定向,不再继续执行后续 rewrite 指令:

rewrite ^/legacy/(.*)\$ https://new.example.com/\ permanent;

这种情况下,如果 replacement 里还有 \,捕获组照样生效。

四、flag 对捕获组的影响

rewrite 指令末尾的 flag(lastbreakredirectpermanent)决定了捕获组之后 Nginx 怎么继续处理:

  • last:停止执行当前 server 块里的 rewrite 指令,用新 URI 重新搜索匹配 location,然后再走一遍 rewrite 流程(最多10轮)。捕获组在 replacement 中正常可用。
  • break:停止执行当前 set ngx_http_rewrite_module 指令,继续在当前 location 里处理请求。捕获组同样可用。
  • redirect:返回 302 重定向,replacement 里的捕获组正常替换后拼接成跳转 URL。
  • permanent:返回 301 重定向,行为同上,只是状态码不同。

在 location 块里使用 last 时要小心:如果同一个 location 里有多条 rewrite 规则,last 会触发重新匹配 location,如果新 location 里还有 rewrite,容易触发"rewrite loop"(超过10次循环返回500错误)。这种情况下一般用 break 更安全。

五、括号嵌套:怎么数 、

捕获组的编号只看括号的位置顺序,不看嵌套层级。从左到右,第一个左括号对应 ,第二个左括号对应 ,以此类推。

rewrite ^/(\w+)/(\d+)/(\w+)\.html$ /\/\/\.html last;

请求 /article/12345/detail.html 匹配后:

  • = article
  • = 12345
  • = detail

结果:/detail/12345/article.html

六、常见错误与避坑

坑1:括号里有特殊字符要转义

正则里的小括号 () 是特殊字符。如果你要匹配字面意义上的左括号 (,需要转义写成 \(

rewrite ^/product/Peanut\(Plain\)/(.*)\$ /product/peanut-plain/\ last;

坑2:} 和 ; 在正则里要加引号

如果正则表达式里包含 };,整个正则要加引号包起来:

if (\ ~ "^/api/endpoint\?(.*)") {
    set \ \;
}

不加引号 Nginx 会把 } 当作配置块结束符来处理,直接报错。

坑3: 在 location 块外可能不生效

捕获组变量 的作用域受限于 rewrite 指令执行上下文。在 server 级别和 location 级别都可以用,但 server 级别的 rewrite 里捕获了 之后,在后面的 location 块里直接用 可能不是你预期值——因为每个 rewrite 阶段都有独立的变量空间。

坑4:capture 组太多超过9个

Nginx 最多只支持到 (第九个捕获组),超过9个的捕获组无法用 这样的语法访问。如果确实需要更多分组,可以考虑把部分逻辑移到 Lua 脚本里处理。

七、实战案例:3个常用场景

案例1:伪静态 URL 重写

rewrite ^/news-(\d+)\.html\$ /article.php?id=\ last;
rewrite ^/product/(\d+)\.html\$ /shop/product?id=\ last;
rewrite ^/tag/([a-z0-9]+)\$ /topic?kw=\ last;

三个经典场景:数字ID重写、商品页重写、标签页重写,分别演示了用捕获组提取数字、纯数字路径和字母数字混合路径。

案例2:移动端适配跳转

if (\ ~* "Mobile|Android|iPhone") {
    set \ "1";
}
if (\ = "1" && \ ~ ^/pc/(.*)\$) {
    rewrite ^/pc/(.*)\$ /mobile/\ last;
}

先判断 UA,再从 URL 路径里用捕获组提取 PC 版路径,拼接成移动版路径后跳转。

案例3:批量旧 URL 迁移

rewrite ^/old/category/(\d+)/page(\d+)\.html\$
    /new/category/\=\ last;

批量迁移时,旧 URL 的分类ID和页码用捕获组分别提取,加到新 URL 里,旧参数用 ? 结尾截断,避免参数残留。

八、总结

  • Nginx rewrite 的捕获组用圆括号 () 定义,从左到右依次生成
  • 捕获组在 rewrite 的 replacement 和 if 指令的正则匹配中都可以使用
  • replacement 末尾加 ? 可以阻止旧参数追加
  • flag 选择要根据 location 嵌套情况来定,避免 rewrite 循环
  • 正则里有特殊字符(()};)要正确转义或加引号
  • 超过9个捕获组的部分无法用 访问

掌握捕获组之后,你会发现 Nginx rewrite 的能力远不止"简单的301跳转"——它可以像程序一样灵活地处理 URL,是网站改版迁移、移动适配、参数重组的利器。


📚 相关文章推荐:
版权声明

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

发表评论