Nginx rewrite 使用 PCRE 正则,匹配对象仅为 URI 路径部分(不含协议、域名、查询参数),括号 () 直接用于捕获,$1、$2 引用分组,需注意 ^ $ 边界控制及转义规则。

Nginx 的 rewrite 规则里,正则表达式用的是 PCRE(Perl Compatible Regular Expressions),但写法和常见工具(比如 JavaScript 或 Python)略有不同,尤其要注意分组捕获、转义、匹配位置这些细节。
关键点:Nginx rewrite 正则默认是匹配整个 URI 路径(不带协议、域名、参数),且只对 $request_uri 的路径部分生效。
URI 路径才是匹配目标
Nginx 的 rewrite 操作对象是请求的 路径部分(即 location 匹配后剩下的 path),不是完整 URL。
例如访问 https://example.com/blog/post/123?from=home,实际参与 rewrite 匹配的是 /blog/post/123,查询参数 ?from=home 默认不参与匹配(除非显式用 $args 或 if ($args ~ ...) 单独判断)。
- ✅ 正确写法(匹配路径):
rewrite ^/old/(.*)$ /new/$1 permanent;
- ❌ 错误写法(试图匹配带参数的完整 URL):
rewrite ^https://.*$ /404.html; # 不生效,协议和域名不在匹配范围内
括号分组要加反斜杠转义才捕获
在 rewrite 指令中,圆括号 () 默认不启用捕获功能,必须写成 \(...\) 才能捕获子串并用于 $1、$2 等变量。
- ✅ 捕获成功(注意反斜杠):
rewrite ^/user/(\d+)/profile$ /profile?id=$1 last; # 访问 /user/123/profile → 重写为 /profile?id=123
- ❌ 没加反斜杠,不捕获:
rewrite ^/user/(\d+)/profile$ /profile?id=$1 last; # 这里 (\d+) 实际是字面量,不是正则捕获 # 正确应写为:^/user/(\d+)/profile$ # 注意:其实上面这行写法在 Nginx 中是合法的——因为 Nginx 的 rewrite 使用 PCRE,原生支持 `()` 捕获,无需反斜杠。 # ⚠️ 更正:这里需要澄清——Nginx 的 rewrite **直接支持 `()` 捕获,不需要写成 `\(...\)`**。之前说法有误。
✅ 正确事实:
Nginx 使用标准 PCRE,() 就是捕获组,$1 对应第一个 (),不需要额外转义。
常见误区来源是旧文档或 Apache mod_rewrite 的习惯(Apache 有时需 \),但在 Nginx 中:
-
(.*)→$1 -
(\d+)→$1 -
(abc|def)→$1
只要在 rewrite 指令的正则里用了 (),就能用 $1 引用。
常用正则写法与避坑提示
^/article/(\d+)$
匹配/article/123,不匹配/article/123/或/article/123/title(结尾严格)^/article/(\d+)/?$
允许结尾可选/^/v1/(.*)$
捕获/v1/xxx/yyy中的xxx/yyy,$1就是该部分^/api/([a-z]+)/(\d+)$
匹配/api/users/100,$1=users,$2=100
⚠️ 注意事项:
-
.匹配任意字符(除换行),如需匹配字面量.,写成\. -
+、*、?是量词,需确保前面有可重复内容(如\d+合法,+单独无效) -
^和$表示开头和结尾,强烈建议加上,避免意外部分匹配(比如^/old会匹配/old-page,而^/old$只匹配/old) -
last、break、redirect、permanent要写在规则末尾,控制后续处理流程
调试技巧:先用 if + return 查看匹配结果
如果不确定正则是否生效,可以临时用 if 配合 return 输出调试信息:
if ($request_uri ~ ^/test/(\d+)) {
return 200 "Matched ID: $1";
}或者用 log_format + $args、$uri 记录日志,再 tail -f 查看:
log_format debug '$remote_addr - $uri $args'; access_log /var/log/nginx/debug.log debug;
这样比反复 reload 更安全高效。
不复杂但容易忽略


















