🚀 第06章 · location 与正则匹配
匹配优先级
location 与正则匹配¶
location 决定"某个请求到底走哪套处理逻辑"——是交给后端代理、直接从磁盘送文件、还是直接拦成 403。它是 Nginx 里最容易被理解偏差、也最容易出事故的指令。本章把 location 的书写和匹配优先级彻底讲清,并拿本机的拦截规则做实证。
location 语法¶
location [修饰符] URI {
# 处理规则
}
URI 可以是一个路径前缀(如 /、/api/),也可以配合修饰符做精确或正则匹配。修饰符决定匹配强度,常见五种:
| 写法 | 含义 | 示例 |
|---|---|---|
= URI |
精确匹配,URI 必须一字不差 | location = /package.json |
^~ URI |
前缀匹配,一旦命中不再试正则 | location ^~ /static/ |
~ URI |
区分大小写的正则 | location ~ ^/(node_modules\|scripts)/ |
~* URI |
不区分大小写的正则 | location ~* \.jpg$ |
(无) URI |
普通前缀匹配 | location / |
五种匹配的优先级¶
这是本章的重中之重。Nginx 挑选 location 的顺序:
- 先找
=精确匹配; - 再找
^~前缀匹配——若命中,直接定案,不再进行下一步; - 再在所有正则匹配里,按书写顺序找到第一个匹配的
~/~*; - 若还没有匹配,退回到长度最长的普通前缀匹配;
- 若连普通前缀都没有,
location /(0 长度,最宽松)兜底。
理解 很多教程说"正则优先于前缀"——那是在同属普通前缀的前提下。一旦
=精确或^~前缀命中,正则就到不了这一步。所以准确排序是:=>^~> 正则 > 普通前缀(最长) >/。
本机 location 实证¶
回看 bangdream.xyz.conf,它使用了全部三类:
# 1) 正则:拦截 node_modules / scripts 目录
location ~ ^/(node_modules|scripts)/ { return 403; }
# 2) 精确:拦截包清单文件
location = /package.json { return 403; }
location = /package-lock.json { return 403; }
# 3) 普通前缀:其余交给后端
location / {
proxy_pass http://127.0.0.1:3000;
}
某个请求到达后,Nginx 会这样裁决:
| 请求 URI | 经过的判定 | 结果 |
|---|---|---|
/package.json |
命中 = 精确 |
403,不透传 |
/node_modules/vue/index.js |
没有 =;正则 ^/(node_modules\|scripts)/ 命中 |
403,不透传 |
/scripts/build.sh |
同上,正则命中 | 403,不透传 |
/main.js |
= 未中;正则未中;走普通前缀 / |
转发到 127.0.0.1:3000 |
理解 注意
/scripts/build.sh这类请求:前缀/本来能匹配它,但因为是普通前缀,优先级低于正则,所以先被正则location ~ ^/(node_modules|scripts)/抢到并return 403。这正是"防泄露规则必须优先于透传规则"的根因。
= 与 ^~ 与普通前缀的区别¶
三者都算"前缀类",但行为不同,是最容易混淆的地方:
| 场景 | 用哪种 | 为什么 |
|---|---|---|
| 只匹配一个精确文件 | = /package.json |
一字不差,最省事,不误伤 |
| 整个目录不能再被正则干扰 | ^~ /static/ |
命中即止,跳过正则遍历 |
| 常规路径分发 | 普通 /api/ |
与其它 location 一起比较长度 |
| 按模式模糊拦截 | ~ / ~* |
需要正则时才用 |
易错
location = /package-lock.json这里如果不写=而写成普通location /package-lock.json,就会变成一个前缀匹配——于是/package-lock.json.bak这类请求也会命中它,虽然本意只想拦那个确切文件。精确就用=,前缀就明写前缀,别混。
常见组合模式¶
静态目录,规则别被正则抢走¶
location ^~ /static/ {
alias /var/www/mydemo/static;
expires 7d;
}
用 ^~ 声明:一旦命中 /static/,直接送文件,不再往下试探任何正则。若前面某处还写了 location ~* \.(css|js)$,这个 ^~ 也能稳稳接管 /static/ 里的资源。
图片按扩展名统一规则¶
location ~* \.(jpg|jpeg|png|gif|webp|svg|ico)$ {
alias /var/www/mydemo/img;
expires 30d;
}
~* 不区分大小写,Logo.PNG 也能命中。
易错 正则是按配置书写顺序依次找"第一个"匹配的。若两个正则都能命中同一个请求,前一个生效,后面的被无视。所以更具体、更精确的正则要写在前面,宽松的放后面。
location 与 proxy_pass 路径拼接¶
前面讲过的"有没有 /"规则,在 location 里最易出错。回顾:
location /api/ {
proxy_pass http://127.0.0.1:3000; # 无斜杠:原样,后端收到 /api/user
}
location /api/ {
proxy_pass http://127.0.0.1:3000/; # 有斜杠:替换,后端收到 /user
}
易错 判断"会不会削路径"只看
proxy_pass末尾——有斜杠会削掉location匹配的前缀,没斜杠原样透传。在正则 location 里用带斜杠的 proxy_pass 会报错,因为 Nginx 无法确定"该替换哪一段";这种场景必须用变量或拆成前缀 location。
调试 location 匹配¶
# 观察实际走了哪个 location(启用 debug 日志,或在 location 里加头)
location /debug { return 200 "here"; }
更推荐用 var 加自定义头测试:
# 在 location 里加一行
add_header X-Matched-Location "proxy-to-3000";
然后:
curl -I https://bangdream.xyz/main.js
# 看响应头里 X-Matched-Location 的值,即知命中了哪个 location
理解 最直接的验证视角是"加一个能自证身份的响应头"。改完
nginx -t+ reload,再curl -I观察这个头,秒懂请求到底去了哪套逻辑。
小结¶
location 的匹配优先级是 = 精确 > ^~ 前缀 > ~/~* 正则(按顺序取第一个) > 普通前缀(取最长)> / 兜底。本机的防泄露规则正是利用"正则优先于普通前缀 /",把敏感路径在透传前拦成 403。写 location 时先问:我要的是精确、还是前缀、还是正则?并保证"更严格的规则写在前面/优先级更高"。下一章讲如何在 location 基础上做访问控制与限流。