Nginx location 优先级详解(附完整示例)
Nginx location 的匹配并非简单地按配置顺序执行,而是一套基于修饰符的优先级规则。其核心优先级顺序为:精确匹配 (=) > 带 ^~ 的最长前缀匹配 > 正则匹配 (~ / ~*,按配置顺序) > 普通最长前缀匹配。
一、location 语法与修饰符
nginx
location [修饰符] 匹配模式 { ... }
| 修饰符 | 含义 | 匹配方式 |
|---|---|---|
= |
精确匹配 | URI 与模式完全相等 |
^~ |
前缀匹配(优先) | URI 以模式开头,命中后不再检查正则 |
~ |
正则匹配(区分大小写) | PCRE 正则 |
~* |
正则匹配(不区分大小写) | PCRE 正则 |
| 无 | 普通前缀匹配 | URI 以模式开头 |
@ |
命名 location | 仅用于内部跳转(try_files、error_page) |
二、核心优先级顺序
1. = 精确匹配 (最高,命中即返回)
2. ^~ 最长前缀匹配 (命中即返回,跳过正则)
3. ~ / ~* 正则匹配 (按配置文件出现顺序,第一个命中即返回)
4. 无修饰符 最长前缀匹配 (记住最长的那个,等正则都失败后才用)
5. / 通用匹配 (兜底)
关键记忆点:
=最优先,命中直接结束。^~命中后短路正则。- 正则按书写顺序,不是按路径长短,第一个匹配的正则赢。
- 普通前缀匹配只是"记住",要等所有正则都失败才回头使用。
三、完整可运行示例
以下配置可直接放入 server {} 块中,用 curl 逐条验证。
nginx
server {
listen 80;
server_name test.local;
# ① 精确匹配(最高优先级)
location = /exact {
return 200 "1. exact match\n";
}
# ② ^~ 前缀匹配(命中后跳过正则)
location ^~ /static/ {
return 200 "2. ^~ prefix match: /static/\n";
}
# ③ 正则匹配(按书写顺序)
location ~ \.php$ {
return 200 "3a. regex (case-sensitive) .php\n";
}
location ~* \.(jpg|png|gif)$ {
return 200 "3b. regex (case-insensitive) image\n";
}
# ④ 普通前缀匹配(最长优先,但要等正则失败)
location /api/ {
return 200 "4a. plain prefix: /api/\n";
}
location /api/v2/ {
return 200 "4b. plain prefix: /api/v2/ (longer)\n";
}
# ⑤ 通用匹配(兜底)
location / {
return 200 "5. default /\n";
}
}
测试结果对照表
| 请求 URI | 命中的规则 | 原因 |
|---|---|---|
/exact |
① = /exact |
精确匹配最高 |
/exact/ |
⑤ / |
精确匹配不匹配,无其他前缀/正则命中 |
/static/a.jpg |
② ^~ /static/ |
^~ 命中后跳过 下面的 \.jpg 正则 |
/static/a.php |
② ^~ /static/ |
同上,^~ 短路所有正则 |
/img/a.JPG |
③ `~* .(jpg | png |
/img/a.php |
③ ~ \.php$ |
正则按顺序,.php 规则写在前面先命中 |
/api/users |
④ = /api/ |
正则都不匹配,回退到最长普通前缀 |
/api/v2/users |
④ = /api/v2/ |
最长前缀优先 |
/anything |
⑤ / |
兜底 |
注意:上表
/api/users命中/api/,/api/v2/users命中/api/v2/(更长的前缀胜出)。但两者都要先经过正则检查,只有正则全失败才会用到它们。
四、易错点:正则 vs 普通前缀
很多人误以为 /api/v2/ 比正则更"具体",所以优先。错! 普通前缀永远排在正则之后。
nginx
server {
# 普通前缀
location /api/ {
return 200 "prefix /api/\n";
}
# 正则:能匹配 /api/xxx.json
location ~ \.json$ {
return 200 "regex .json\n";
}
}
- 请求
/api/data.json→ 命中 正则 (输出regex .json),而不是/api/。 - 请求
/api/data→ 正则不匹配 → 回退到/api/。
如果想锁定前缀不被正则抢走 ,加 ^~:
nginx
location ^~ /api/ {
return 200 "locked prefix\n";
}
# 此时 /api/data.json 也走这里,不会进 ~ \.json$
五、易错点:正则顺序,而非长度
nginx
location ~ \.png$ {
return 200 "A: lowercase png only\n";
}
location ~* \.png$ {
return 200 "B: case-insensitive png\n";
}
| 请求 | 结果 | 说明 |
|---|---|---|
/a.png |
A | 第一个正则就命中(小写 png) |
/a.PNG |
B | A 区分大小写失败,落到 B |
如果两个都写 ~* 且都能匹配,永远只有第一个生效,第二个是死代码。
六、完整匹配流程图
请求 URI
│
▼
[1] 查 = 精确匹配 ──命中──▶ 使用,结束
│未命中
▼
[2] 遍历所有前缀匹配(含 ^~ 和普通),记录"最长前缀"
│
▼
[3] 最长前缀是否为 ^~ ?──是──▶ 使用该 ^~ 规则,结束
│否
▼
[4] 按顺序遍历 ~ / ~* 正则 ──第一个命中──▶ 使用,结束
│全不命中
▼
[5] 使用 [2] 记录的最长普通前缀 ──存在──▶ 使用,结束
│不存在
▼
[6] 使用 location / 兜底
七、常见实战片段
1. 静态资源 + SPA 路由
nginx
server {
root /var/www/dist;
# 静态资源:^~ 防止被后面的正则或 SPA 兜底抢走
location ^~ /assets/ {
expires 30d;
add_header Cache-Control "public, immutable";
}
# 图片缓存
location ~* \.(jpg|jpeg|png|gif|webp|svg)$ {
expires 7d;
}
# API 反向代理
location /api/ {
proxy_pass http://backend;
}
# SPA 兜底(放在最后)
location / {
try_files $uri $uri/ /index.html;
}
}
2. 禁止访问隐藏文件(正则优先于通用前缀)
nginx
location ~ /\.(git|env|htaccess) {
deny all;
}
location / {
try_files $uri $uri/ =404;
}
3. 精确匹配做健康检查(性能最高)
nginx
location = /healthz {
access_log off;
return 200 "ok";
}
4. 命名 location 用于内部跳转
nginx
location / {
try_files $uri $uri/ @fallback;
}
location @fallback {
proxy_pass http://backend;
}
命名 location(
@name)不参与 普通 URI 匹配,只能被try_files、error_page、internal内部跳转触发。
八、速查总结
| 场景 | 正确做法 |
|---|---|
单一入口(如 /health、/login) |
location = /health |
| 目录要"锁定",不被正则抢 | location ^~ /static/ |
| 按扩展名处理 | `location ~* .(jpg |
| 多个正则都能匹配 | 注意书写顺序,第一个胜出 |
| SPA / 兜底 | location / 放最后 |
| 内部跳转 | location @name |
一句话记忆:= 绝对优先 → ^~ 锁前缀 → 正则看顺序 → 普通前缀看最长 → / 兜底。