通过实际示例解释 Nginx Location 匹配规则
你可能调整过 Nginx 配置,重新加载后却疑惑为什么你的 location 块似乎没有生效。也许静态文件请求打到了 PHP 处理器,或者 API 路由被一个 catch-all 吞掉了。罪魁祸首通常是 Nginx 的 location 匹配算法——它并不像看起来那么直接。
在本指南中,我们将通过可以测试的实际示例,分解 Nginx 如何选择 location 块。到最后,你将确切知道哪个块胜出以及为什么。
Nginx Location 匹配如何工作
当请求到达时,Nginx 会将 URI 与所有定义的 location 块进行比较。匹配过程遵循特定顺序:
- 精确匹配 (
=) — 最高优先级。如果找到,Nginx 停止并使用它。 - 最长前缀匹配 — Nginx 记住最长的匹配前缀 location。
- 正则表达式匹配 (
~或~*) — 按出现顺序检查。第一个匹配的正则胜出,覆盖前缀匹配(除非使用了^~)。 - 带
^~的前缀匹配 — 如果最长匹配前缀带有^~,Nginx 跳过正则检查并使用它。 - 如果没有正则匹配,则使用最长前缀匹配。
这就是核心算法。让我们看看它的实际应用。
Location 修饰符:快速参考
| 修饰符 | 语法 | 匹配类型 | 优先级 |
|---|---|---|---|
= | location = /path | 精确 | 最高 |
^~ | location ^~ /path | 前缀(无正则) | 高 |
~ | location ~ \.php$ | 正则(区分大小写) | 中 |
~* | location ~* \.(jpg|png)$ | 正则(不区分大小写) | 中 |
| 无 | location /path | 前缀 | 最低 |
实际示例 1:静态文件 vs. PHP
考虑这个常见设置:
server {
listen 80;
server_name example.com;
location / {
root /var/www/html;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
}
对 /index.php 的请求匹配正则 \.php$,因此它转到 PHP-FPM。对 /logo.png 的请求不匹配正则,因此回退到前缀 / 并提供静态文件。很简单,对吧?
但是如果你想将 /uploads/photo.php 作为静态文件提供(也许它实际上是一个图像)呢?你可以添加一个精确匹配:
location = /uploads/photo.php {
root /var/www/html;
}
现在这个精确匹配优先于正则。
实际示例 2:使用前缀和正则的 API 路由
假设你有一个 /api/ 下的 API,并且你想将所有请求代理到后端,除了一个返回静态响应的健康检查。
location = /api/health {
return 200 "OK";
}
location /api/ {
proxy_pass http://backend;
}
location ~ ^/api/v[0-9]+/special {
proxy_pass http://special-backend;
}
让我们追踪 /api/health:精确匹配胜出,返回 200。对于 /api/users:没有精确匹配,没有正则匹配(不匹配 special),因此使用最长前缀 /api/。对于 /api/v1/special:正则匹配,因此它覆盖前缀并转到 special-backend。
这演示了正则如何覆盖前缀匹配。如果你希望前缀始终胜出,你可以使用 ^~ 代替。
实际示例 3:^~ 的力量
想象你有一个目录 /static/,其中的文件永远不应由 PHP 处理,即使它们以 .php 结尾。使用 ^~ 确保 Nginx 不检查正则 location。
location ^~ /static/ {
root /var/www/html;
}
location ~ \.php$ {
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
}
对 /static/script.php 的请求匹配前缀 ^~ /static/。由于 ^~,Nginx 跳过正则检查并直接提供文件。如果没有 ^~,正则将匹配并且 PHP-FPM 将执行它——一个潜在的安全风险。
常见陷阱及如何避免它们
- 正则顺序很重要: Nginx 按照配置中出现的顺序检查正则 location。将更具体的模式放在前面。
- 缺少尾部斜杠:
location /api和location /api/是不同的。前者匹配/api和/apix,而后者匹配/api/和/api/users。要精确。 - 重叠前缀: 最长前缀胜出,因此对于
/api/v1/users,location /api/v1优先于location /api。 - 忘记为静态资源使用
^~: 使用它来防止正则劫持静态文件请求。 - 大小写敏感性:
~区分大小写,~*不区分。对于文件扩展名使用~*。
调试 Location 匹配
如果你不确定正在使用哪个 location,请在 Nginx 中启用调试日志。在你的 server 块中添加 error_log /var/log/nginx/error.log debug;,重新加载,并检查日志。你会看到类似 test location: "/api/users" 和 using configuration "/api/" 的行。
或者,在每个 location 中临时使用 return 200 "matched: /api/"; 来查看哪个响应。
常见问题
Nginx location 修饰符的优先级顺序是什么?
精确匹配 (=) 最高,然后是带 ^~ 的最长前缀,然后是按顺序的正则 (~ 或 ~*),最后是不带 ^~ 的最长前缀匹配。
正则 location 可以覆盖前缀 location 吗?
是的,除非前缀 location 使用了 ^~。正则 location 在最长前缀匹配之后检查,如果正则匹配,它优先于常规前缀。
如何仅针对特定文件匹配 location?
使用 = 进行精确匹配,例如 location = /favicon.ico。这确保只匹配该精确 URI。
掌握你的 Nginx 配置
理解 location 匹配是可靠 Nginx 设置的关键。使用 nginx -t 测试你的配置,并在有疑问时使用调试日志。如果你正在处理 Nginx 日志并需要分析流量模式,试试我们的 Nginx 日志分析器 来快速解析和可视化你的日志。