Skip to content

深入理解 Nginx location 匹配规则与代理配置

本文是 Nginx 实操专栏的第三篇,我们将深入探讨 location 匹配规则、优先级以及 proxy_pass 的使用方法,帮助你掌握 Nginx 最核心的功能之一。

引言

在前面两篇文章中,我们分别介绍了 Nginx 的架构原理和配置结构。今天我们将深入探讨 Nginx 最重要的功能之一:location 匹配规则和代理配置。这是构建复杂 Web 应用和 API 网关的基础。

location 匹配规则详解

location 指令用于根据 URI 匹配不同的请求,并执行相应的处理逻辑。理解 location 的匹配规则对正确配置 Nginx 至关重要。

location 匹配语法

nginx
location [modifier] uri { ... }

其中 modifier 是可选的修饰符,决定了匹配的方式。

匹配修饰符类型

1. 精确匹配 (=)

nginx
location = / {
    # 只匹配 "/"
    # 例如: http://example.com/
}

这是优先级最高的匹配方式,一旦匹配成功,不再检查其他 location。

2. 前缀匹配 (无修饰符)

nginx
location /documents/ {
    # 匹配以 "/documents/" 开头的所有 URI
    # 例如: /documents/, /documents/file.html, /documents/subdir/file.pdf
}

3. 优先前缀匹配 (^~)

nginx
location ^~ /images/ {
    # 匹配以 "/images/" 开头的 URI
    # 如果匹配成功,则不再进行正则表达式匹配
}

4. 正则表达式匹配 (~ 和 ~*)

nginx
# 区分大小写的正则匹配
location ~ \.(gif|jpg|png)$ {
    # 匹配以 .gif, .jpg, .png 结尾的请求
}

# 不区分大小写的正则匹配
location ~* \.(GIF|JPG|PNG)$ {
    # 匹配以 .GIF, .JPG, .PNG 结尾的请求(忽略大小写)
}

匹配优先级规则

Nginx 按照以下顺序进行 location 匹配:

  1. 精确匹配 (=) - 优先级最高
  2. 优先前缀匹配 (^~) - 匹配成功后停止正则匹配
  3. 正则表达式匹配 (~ 和 ~*) - 按照配置文件中的顺序进行匹配
  4. 最长前缀匹配 - 选择匹配长度最长的前缀

实际匹配示例

nginx
server {
    listen 80;
    server_name example.com;
    
    # 精确匹配
    location = / {
        # 只匹配 http://example.com/
        return 200 "Exact match for /\n";
    }
    
    # 优先前缀匹配
    location ^~ /images/ {
        # 匹配 /images/ 开头的所有请求
        # 即使有正则匹配 /images/\.png$,也不会被执行
        return 200 "Prefix match for /images/\n";
    }
    
    # 正则表达式匹配
    location ~ \.(png|jpe?g)$ {
        return 200 "Regex match for images\n";
    }
    
    # 普通前缀匹配
    location /documents/ {
        return 200 "Longest prefix match for /documents/\n";
    }
    
    # 默认匹配
    location / {
        return 200 "Default match for /\n";
    }
}

对于请求 http://example.com/images/logo.png,虽然同时匹配了 ^~/images/\.(png|jpe?g)$,但由于 ^~ 修饰符的存在,会选择第一个匹配项。

proxy_pass 详解

proxy_pass 是 Nginx 反向代理的核心指令,用于将请求转发给后端服务器。

基本用法

nginx
location /api/ {
    proxy_pass http://backend_server;
}

URI 处理规则

proxy_pass 的行为取决于其后是否有 URI 路径:

1. 不带 URI 路径的 proxy_pass

nginx
location /api/ {
    proxy_pass http://backend;  # 注意末尾没有 /
}

在这种情况下,请求的完整 URI 会被传递给后端服务器:

  • 请求: http://example.com/api/users/123
  • 转发到: http://backend/api/users/123

2. 带 URI 路径的 proxy_pass

nginx
location /api/ {
    proxy_pass http://backend/;  # 注意末尾有 /
}

在这种情况下,location 匹配的部分会被替换为 proxy_pass 指定的 URI:

  • 请求: http://example.com/api/users/123
  • 转发到: http://backend/users/123

实际示例对比

nginx
# 示例 1: 不带 URI 路径
location /api/ {
    proxy_pass http://backend;
}
# /api/users -> http://backend/api/users

# 示例 2: 带 URI 路径
location /api/ {
    proxy_pass http://backend/;
}
# /api/users -> http://backend/users

# 示例 3: 精确匹配
location = /api {
    proxy_pass http://backend;
}
# /api -> http://backend/api

# 示例 4: 前缀匹配不以 / 结尾
location /api {
    proxy_pass http://backend/;
}
# /api -> http://backend//
# /api/users -> http://backend//users

反向代理常用配置

基本代理头设置

nginx
location /api/ {
    proxy_pass http://backend/;
    
    # 传递真实的主机名
    proxy_set_header Host $host;
    
    # 传递真实的客户端 IP
    proxy_set_header X-Real-IP $remote_addr;
    
    # 传递完整的代理链 IP
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    
    # 传递使用的协议 (http/https)
    proxy_set_header X-Forwarded-Proto $scheme;
}

超时和缓冲配置

nginx
location /api/ {
    proxy_pass http://backend/;
    
    # 连接超时时间
    proxy_connect_timeout 30s;
    
    # 发送超时时间
    proxy_send_timeout 30s;
    
    # 读取超时时间
    proxy_read_timeout 30s;
    
    # 启用响应缓冲
    proxy_buffering on;
    
    # 缓冲区大小
    proxy_buffer_size 4k;
    proxy_buffers 8 4k;
    
    # 临时文件最大大小
    proxy_max_temp_file_size 1024m;
}

负载均衡配置

nginx
# 定义上游服务器组
upstream backend {
    server 192.168.1.10:8080 weight=3;
    server 192.168.1.11:8080;
    server 192.168.1.12:8080 backup;
}

location /api/ {
    proxy_pass http://backend/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

高级代理技巧

条件代理

nginx
location /api/ {
    # 根据请求方法进行不同处理
    if ($request_method = POST) {
        proxy_pass http://write_backend;
        break;
    }
    
    proxy_pass http://read_backend;
}

动态代理

nginx
location /proxy/ {
    # 从 URI 中提取目标地址
    rewrite ^/proxy/(.*)$ /$1 break;
    proxy_pass http://$1;
}

错误处理

nginx
location /api/ {
    proxy_pass http://backend/;
    
    # 自定义后端错误页面
    proxy_intercept_errors on;
    error_page 500 502 503 504 /backend_error.html;
}

实际应用场景

微服务网关配置

nginx
upstream user_service {
    server 192.168.1.10:3000;
}

upstream order_service {
    server 192.168.1.11:3000;
}

upstream product_service {
    server 192.168.1.12:3000;
}

server {
    listen 80;
    server_name api.example.com;
    
    # 用户服务
    location /api/users/ {
        proxy_pass http://user_service/;
        proxy_set_header Host $host;
    }
    
    # 订单服务
    location /api/orders/ {
        proxy_pass http://order_service/;
        proxy_set_header Host $host;
    }
    
    # 产品服务
    location /api/products/ {
        proxy_pass http://product_service/;
        proxy_set_header Host $host;
    }
    
    # 默认服务
    location / {
        return 404 "API endpoint not found";
    }
}

WebSocket 代理

nginx
location /ws/ {
    proxy_pass http://websocket_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
}

文件上传代理

nginx
location /upload/ {
    proxy_pass http://file_backend/;
    
    # 增加客户端最大 body 大小限制
    client_max_body_size 100m;
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

调试技巧

查看匹配结果

nginx
location /test {
    add_header X-Location-Match "Matched /test";
    return 200 "Location matched\n";
}

记录代理信息

nginx
location /api/ {
    proxy_pass http://backend/;
    
    # 在日志中记录代理信息
    log_format proxy '$remote_addr - $remote_user [$time_local] '
                   '"$request" $status $body_bytes_sent '
                   '"$http_referer" "$http_user_agent" '
                   '"Proxy to: $upstream_addr" "Response time: $upstream_response_time"';
    
    access_log /var/log/nginx/proxy.log proxy;
}

最佳实践

1. 明确的匹配规则

nginx
# 好的做法:明确指定匹配类型
location = /favicon.ico {
    # 精确匹配 favicon
}

location ^~ /static/ {
    # 静态资源,不需要正则匹配
}

location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ {
    # 正则匹配静态资源
}

2. 合理的代理配置

nginx
location /api/ {
    proxy_pass http://backend/;
    
    # 必要的代理头
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    
    # 合理的超时设置
    proxy_connect_timeout 30s;
    proxy_send_timeout 30s;
    proxy_read_timeout 30s;
    
    # 启用缓冲提高性能
    proxy_buffering on;
}

总结

掌握 location 匹配规则和 proxy_pass 的使用是精通 Nginx 的关键步骤。通过合理配置,我们可以实现:

  1. 精确的请求路由
  2. 高效的反向代理
  3. 灵活的负载均衡
  4. 安全的微服务网关

在下一章中,我们将探讨 HTTPS 配置和安全加固的相关内容,帮助你构建更加安全可靠的 Web 服务。

要点回顾:

  1. location 匹配有四种类型,优先级依次为 = > ^~ > ~,~* > 无修饰符
  2. proxy_pass 是否带有 URI 路径会影响 URI 的处理方式
  3. 正确设置代理头对于后端服务识别真实客户端信息至关重要
  4. 合理配置超时和缓冲参数可以提高代理性能