Nginx 的 location 指令如何匹配?优先级是什么?
Nginx 的 location 指令如何匹配?优先级是什么?
location 是 Nginx 中最核心的指令之一,它决定了一个请求由哪个配置块来处理。理解它的匹配规则和优先级,不仅是面试高频考点,更是排查 Nginx 配置问题的基本功。
location 的四种匹配方式
location 指令的语法:location [=|~|~*|^~] uri { ... }
修饰符不同,匹配行为完全不同:
| 修饰符 | 匹配方式 | 匹配后是否继续搜索 |
|---|---|---|
= | 精确匹配 | 否,立即停止 |
^~ | 前缀匹配 | 否,跳过正则检查 |
~ | 正则匹配(区分大小写) | 否,按配置顺序首个命中即停止 |
~* | 正则匹配(不区分大小写) | 否,按配置顺序首个命中即停止 |
| 无 | 前缀匹配 | 是,继续检查正则 |
精确匹配(=)
只有请求 URI 与指定路径完全一致时才命中,一旦匹配立即停止搜索,性能最优:
nginxlocation = / { # 仅匹配 /,不匹配 /index.html }
前缀匹配(无修饰符)
按 URI 前缀匹配,匹配成功后不会立即使用,而是先记住这个最长前缀匹配,继续检查正则表达式。如果正则没有命中,才会回退使用这个前缀匹配:
nginxlocation /docs/ { # 匹配以 /docs/ 开头的所有 URI # 但如果有正则也命中了,正则优先 }
正则匹配(~ 和 ~*)
~:区分大小写~*:不区分大小写
正则匹配按配置文件中出现的顺序依次检查,首个命中即停止:
nginxlocation ~ \.php$ { # 区分大小写,匹配 .php 结尾的请求 } location ~* \.(jpg|png|gif|css|js)$ { # 不区分大小写,匹配常见静态资源 }
此外还有 !~ 和 !~*,表示正则不匹配,但它们不能用于 location 指令,只能用在 if 条件判断中。
前缀匹配(^~)
行为与无修饰符的前缀匹配类似,但关键区别是:如果 ^~ 前缀匹配成功,会跳过后续所有正则检查,直接使用该 location。这在对性能敏感的场景下非常有用:
nginxlocation ^~ /static/ { # 匹配 /static/ 开头的请求 # 即使有正则也匹配,也不检查,直接用这个 }
Nginx 完整匹配算法
面试中光背优先级顺序不够,必须理解 Nginx 的完整匹配流程:
- Nginx 首先检查所有前缀匹配(包括
=、^~和无修饰符),找到最长前缀匹配 - 如果最长前缀是精确匹配(
=),立即使用,匹配结束 - 如果最长前缀是
^~匹配,立即使用,匹配结束 - 按配置文件顺序依次检查所有正则表达式(
~和~*) - 如果正则命中,使用该正则 location,匹配结束
- 如果所有正则都未命中,回退使用步骤 1 中找到的最长前缀匹配
这个流程说明一个关键点:正则匹配的优先级高于普通前缀匹配,但低于 = 和 ^~。
优先级总结(从高到低)
- 精确匹配
=— 最高优先级,匹配即停 - 前缀匹配
^~— 匹配后跳过正则检查 - 正则匹配
~/~*— 按配置顺序,先到先得 - 普通前缀匹配 — 优先级最低,作为兜底
注意:正则匹配之间没有优先级之分,完全取决于配置文件中的书写顺序,写在前面的先匹配。
配置示例与匹配结果
nginxserver { listen 80; server_name example.com; location = / { return 200 "1: exact /"; } location / { return 200 "5: prefix /"; } location ^~ /images/ { return 200 "2: ^~ /images/"; } location ~ \.php$ { return 200 "3: regex .php"; } location ~* \.(jpg|png|gif)$ { return 200 "4: regex image"; } location /docs/ { return 200 "6: prefix /docs/"; } }
匹配结果验证:
| 请求 URI | 命中 location | 原因 |
|---|---|---|
/ | = / | 精确匹配,优先级最高 |
/images/logo.jpg | ^~ /images/ | ^~ 命中后跳过正则 |
/api/test.php | ~ \.php$ | 前缀匹配 / 记住后,正则命中 |
/photo.JPG | ~* \.(jpg|png|gif)$ | 不区分大小写正则命中 |
/docs/readme.html | /docs/ | 最长前缀 /docs/ 优先于 /,且无正则命中 |
/about | / | 仅前缀 / 命中,无正则匹配 |
常见面试陷阱
陷阱一:前缀匹配的长度优先
多个普通前缀同时匹配时,Nginx 选择最长前缀,而非配置顺序:
nginxlocation /api/ { ... } # 前缀长度 5 location /api/v1/ { ... } # 前缀长度 9,优先
请求 /api/v1/users 会匹配 /api/v1/,因为前缀更长。
陷阱二:正则覆盖普通前缀
nginxlocation /images/ { ... } location ~* \.(jpg|png)$ { ... }
请求 /images/logo.jpg 会命中正则 ~*,而非前缀 /images/,因为正则优先级高于普通前缀。如果希望 /images/ 下的请求不被正则抢走,必须使用 ^~。
陷阱三:= 只匹配精确路径
nginxlocation = /api { ... }
它只匹配 /api,不匹配 /api/、/api/v1。如果需要同时匹配,应该用前缀匹配。
实际配置建议
- 高频路径用精确匹配
=,性能最优且语义清晰 - 静态资源目录用
^~,避免被正则拦截 - 正则匹配控制数量,按命中频率从高到低排列
- 避免在正则中使用复杂回溯表达式,防止 ReDoS 攻击
- 通用兜底
location /放在最后
nginx# 精确匹配首页 location = / { proxy_pass http://frontend; } # 静态资源,跳过正则检查 location ^~ /static/ { alias /var/www/static/; expires 30d; } # PHP 请求转发 location ~ \.php$ { fastcgi_pass unix:/run/php/php-fpm.sock; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } # 管理后台,跳过正则检查 location ^~ /admin/ { auth_basic "Restricted"; auth_basic_user_file /etc/nginx/.htpasswd; proxy_pass http://admin_backend; } # API 代理 location /api/ { proxy_pass http://api_backend; } # 兜底 location / { try_files $uri $uri/ /index.html; }
追问:location 嵌套怎么用?
location 支持嵌套,但只有普通前缀匹配可以嵌套在普通前缀内,正则和精确匹配不能嵌套:
nginxlocation /api/ { proxy_pass http://backend; location /api/internal/ { # 更具体的路径,覆盖外层配置 deny all; } }
内层 location 会完全覆盖外层的处理逻辑,而不是继承。如果内层也需要 proxy_pass,必须显式重新声明。
追问:location 和 rewrite 的执行顺序?
Nginx 处理请求时,rewrite 阶段在 location 匹配之前执行(server 级别的 rewrite)。如果 rewrite 修改了 URI,location 会基于修改后的 URI 重新匹配。但 location 内部的 rewrite 可能触发重新匹配,需要注意避免循环重写。