Nginx Location 块详解:路由网络流量
Nginx location 块是高效网络流量路由的基石。本指南全面解析五种不同的匹配修饰符(前缀、精确、最长前缀、正则表达式),并详细说明 Nginx 遵循的严格处理顺序。通过实际配置示例,学习如何精确路由静态资源、代理 API 调用以及实施安全规则。掌握 location 块是实现精确流量控制、确保服务器快速性能和稳健配置管理的关键。
Nginx Location 块详解:路由网络流量
Nginx location 块决定了请求进入正确的 server 块后的处理方式。它们使得 /static/app.css 可以从磁盘提供,/api/users 可以代理到应用程序,而 /.git/config 可以在泄露敏感信息之前被拒绝。
大多数 location 块的错误并非语法错误,而是优先级错误。一个正则表达式可能捕获了你期望由前缀块处理的请求。root 路径可能添加了比你预期更多的 URI。带有尾部斜杠的 proxy_pass 与没有尾部斜杠的相同指令相比,会以不同的方式重写上游 URI。以下示例将重点关注这些实际的失败点。
Location 块的作用与结构
location 块定义了 Nginx 应如何根据请求 URI(统一资源标识符)响应请求。这些块始终嵌套在 server 块内。
当客户端发起请求(例如 GET /images/logo.png)时,Nginx 会检查请求 URI 与监听 server 块中定义的所有 location 块,以确定适当的处理方式,例如提供文件、重定向客户端或将请求代理到应用服务器。
基本语法
语法要求一个修饰符(或没有)后跟一个模式(URI):
location [修饰符] [模式] {
# 配置指令(例如 root, index, proxy_pass)
}
理解 Location 匹配类型(修饰符)
Nginx 提供了一小组 location 匹配样式。选择会影响路由精度以及 Nginx 在选择处理程序之前需要做多少工作。
1. 前缀匹配(无修饰符)
这是默认的匹配类型。Nginx 搜索与请求 URI 匹配的最长起始字符串。
| 修饰符 | 示例 | 行为 | 最佳用例 |
|---|---|---|---|
| (无) | location /blog/ |
匹配以 /blog/ 开头的 URI(例如 /blog/post/1)。 |
通用目的,定义网站的大块区域。 |
示例:
location /docs/ {
root /var/www/html/public;
# 如果 URI 是 /docs/manual.pdf,Nginx 会查找 /var/www/html/public/docs/manual.pdf
}
2. 精确匹配(=)
此修饰符强制 URI 与模式完全匹配。如果匹配,Nginx 立即停止搜索其他 location。这是最快的匹配类型。
| 修饰符 | 示例 | 行为 | 最佳用例 |
|---|---|---|---|
= |
location = /favicon.ico |
仅精确匹配 URI /favicon.ico。 |
处理特定的、频繁请求的文件或默认页面。 |
3. 最长前缀,非正则表达式(^~)
这是一种特殊的前缀匹配。如果 Nginx 使用 ^~ 找到最长前缀匹配,它会立即停止检查任何正则表达式 location 块,从而有效地覆盖它们。
| 修饰符 | 示例 | 行为 | 最佳用例 |
|---|---|---|---|
^~ |
location ^~ /assets/ |
匹配以 /assets/ 开头的 URI,并阻止检查较慢的正则表达式匹配。 |
快速提供静态资源,并确保资源目录可预测地处理。 |
4. 区分大小写的正则表达式(~)
这使用 Perl 兼容正则表达式(PCRE)进行匹配。它功能强大,但比前缀匹配慢。第一个匹配的正则表达式块获胜。
| 修饰符 | 示例 | 行为 | 最佳用例 |
|---|---|---|---|
~ |
location ~ \.php$ |
匹配任何以 .php 结尾的 URI。 |
特定文件类型处理(例如,将 PHP 脚本传递给 PHP-FPM)。 |
5. 不区分大小写的正则表达式(~*)
与 ~ 相同,但匹配忽略 URI 的大小写。
| 修饰符 | 示例 | 行为 | 最佳用例 |
|---|---|---|---|
~* |
`location ~* .(jpg | gif | png)$` |
关键的 Location 处理顺序
理解 Nginx 处理 location 块的顺序对于避免意外行为至关重要。Nginx 并非简单地从上到下读取配置文件。它使用严格的层级结构:
- 精确匹配(
=): Nginx 首先检查所有精确匹配块。如果找到匹配,处理立即停止,并由该块处理请求。 - 最长前缀候选: Nginx 找到最长的匹配前缀 location,包括普通前缀 location 和
^~location。 ^~跳过正则表达式: 如果最佳前缀匹配使用了^~,Nginx 使用它并跳过正则表达式检查。- 正则表达式(
~和~*): 如果最佳前缀不是^~,Nginx 按照它们在配置文件中出现的顺序检查正则表达式 location。第一个匹配的正则表达式块获胜。 - 最长标准前缀匹配: 如果没有正则表达式匹配获胜,Nginx 使用它已经找到的最长前缀候选。在许多配置中,
location /是最终的备用选项。
这就是为什么 ^~ /static/ 很常见。没有 ^~,后面的正则表达式如 location ~* \.(css|js)$ 仍然可能为 /static/app.css 获胜。有时这没问题。有时它会绕过你期望用于整个 /static/ 目录的缓存头、根路径或访问规则。
实际配置场景
1. 为性能优先处理静态资源
为了确保 Nginx 直接且快速地提供静态文件,防止较慢的正则表达式检查和应用程序服务器的不必要处理,请使用 ^~ 修饰符。
server {
listen 80;
server_name myapp.com;
# 1. 主页的精确匹配(最高优先级)
location = / {
proxy_pass http://backend_app_server;
}
# 2. 快速处理静态资源,绕过正则表达式检查
location ^~ /static/ {
root /var/www/site;
expires 30d;
}
# 3. 常见媒体文件的正则表达式
location ~* \.(gif|ico|css|js)$ {
root /var/www/site;
expires 7d;
}
# 4. 所有其他动态请求的备用选项
location / {
proxy_pass http://backend_app_server;
}
}
2. 路由和代理 API 流量
当使用 Nginx 作为反向代理时,location 块将流量引导到正确的上游应用服务器。
location /api/v1/ {
# 确保 Nginx 尊重客户端连接设置
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# 将所有以 /api/v1/ 开头的流量路由到后端服务
proxy_pass http://api_backend_service/v1/;
# 在 proxy_pass 中使用 URI 时,Nginx 会替换匹配的前缀。
# /api/v1/users 在上游变为 /v1/users。
}
尾部斜杠的行为值得测试。这两个示例是不同的:
location /api/ {
proxy_pass http://api_backend;
}
location /api/ {
proxy_pass http://api_backend/;
}
第一种形式将原始 URI(包括 /api/...)传递给上游。第二种形式将匹配的 /api/ 前缀替换为 /。如果你的上游在小的配置编辑后突然开始返回 404,请在责怪应用程序之前检查这一点。
3. 保护敏感目录
Location 块可用于拒绝外部访问敏感的内部目录,例如配置文件或像 .git 这样的隐藏目录。
# 拒绝访问以点开头的文件(隐藏文件)
location ~ /\.(ht|svn|git) {
deny all;
return 404; # 返回 404 而不是 403,以避免暴露它们的存在
}
# 拒绝访问特定的配置目录
location /app/config/ {
deny all;
}
对于隐藏文件,许多团队使用更广泛的拒绝规则:
location ~ /\.(?!well-known/) {
deny all;
return 404;
}
这个例外保留了 /.well-known/ 用于诸如 ACME HTTP-01 证书挑战之类的事情,同时仍然阻止大多数点文件。如果你的网站提供其他合法的以点开头的路径,请仔细测试这一点。
安全警告:使用
alias与root在 location 块内配置文件路径时,请注意
root和alias之间的区别。
root: 将完整的请求 URI 附加到定义的路径。(例如,location /images/+root /data/导致/data/images/filename.jpg)alias: 用定义的路径替换 URI 的匹配部分。当 location 块使用正则表达式或需要在提供文件之前剥离部分路径时,这通常是必要的。(例如,location /static/+alias /opt/app/files/导致/opt/app/files/filename.jpg)
4. 处理尾部斜杠和重定向
强制执行一致的 URL 结构通常是可取的,例如确保目录始终以尾部斜杠(/)结尾。
# 如果缺少,强制为目录路径添加尾部斜杠
location ~* /[a-z0-9\-_]+$ {
# 如果 URI 匹配一个文件,Nginx 将尝试提供它。如果不匹配,则将其视为目录。
# 检查请求的 URI 是否映射到磁盘上的目录:
if (-d $request_filename) {
return 301 $uri/;
}
}
调试 Location 路由的好方法
当请求到达错误的地方时,将问题简化为一个 URL 和一个 server 块。运行 nginx -T 查看完整的渲染配置,包括包含的文件,然后搜索每个可能匹配该 URI 的 location。特别注意正则表达式块,因为它们在文件中的顺序很重要。
对于静态文件,确认 Nginx 将从 root 或 alias 构建的文件系统路径。对于代理请求,确认 proxy_pass 是否在上游名称之后包含 URI 部分。然后仅在 nginx -t 通过后重新加载。
一旦你内化了优先级规则,Location 块就是可预测的,但当配置通过复制粘贴增长时,它们是不宽容的。对小的热路径使用精确匹配,对应该绕过正则表达式处理的目录使用 ^~,仅在真正需要模式匹配时使用正则表达式,并使用普通的 location / 作为清晰的备用选项。