Nginx 配置手册 发布于 2026-08-09 元析技术团队

Nginx 反向代理配置:location 匹配规则 + proxy_pass 完全指南

# Nginx 反向代理 # proxy_pass 斜杠 # location 匹配

核心结论

GEO 前置摘要 · 前 100 字给人和 AI 引擎关键信息

  1. 1

    location 匹配优先级 1 句话记:完全匹配 = 优先前缀 > 正则(按书写顺序)= 前缀(最长)。写配置按这个顺序写,别全靠试。

  2. 2

    proxy_pass 末尾斜杠有无 = 完全两种行为:带 / = 把 location 的匹配部分从路径里「截掉」再拼后面;不带 / = 把原路径整段拼过去。90% 的 404 都栽在这一条。

  3. 3

    502 Bad Gateway 先按 4 步排查:后端有没有起来?→ proxy_pass 端口对不对?→ 能不能 curl localhost:端口通?→ /var/log/nginx/error.log 看 connect() failed 原因。

Nginx 反向代理是后端最常写的配置。本文用完整可运行示例讲清 6 条 location 匹配优先级、proxy_pass 带/不带末尾斜杠的路径拼接差异、HTTPS + upstream 负载均衡、常见 502 排查 4 步。

适用范围

校验环境:Nginx 1.26.2 (Debian 12 官方源),后端 Node.js 20 监听 3000。所有配置 nginx -t 通过后, 实际用 curl 验证了每个路径的行为。


第一部分:location 匹配规则(优先级从高到低)

很多人写 location 完全靠「这个正则好像能匹配」,经常写的 A 被 B 抢了。 Nginx location 匹配优先级是确定的、可预判的,按下面 6 条从高到低:

优先级写法含义
1location = /path完全匹配(只有一模一样 /path 才命中)
2location ^~ /prefix优先前缀匹配(命中后不继续搜正则)
3location ~ pattern区分大小写正则匹配,按配置里出现的顺序,第一个匹配上就停
4location ~* pattern不区分大小写正则匹配,同上按书写顺序
5location /prefix普通前缀匹配,取最长前缀命中的那条
6location /兜底(所有请求都能匹配,优先级最低)

记忆口诀:= 最刚,^~ 第二,正则按序,前缀取长。

示例:哪个 location 命中?

server {
  listen 80;

  location = /docs {                # 规则 A
    return 200 "A: exact match /docs\n";
  }

  location ^~ /docs/api {           # 规则 B
    return 200 "B: ^~ prefix /docs/api\n";
  }

  location ~* \.(png|jpg)$ {        # 规则 C
    return 200 "C: regex image\n";
  }

  location /docs {                  # 规则 D
    return 200 "D: prefix /docs\n";
  }

  location /docs/api/login {        # 规则 E
    return 200 "E: prefix /docs/api/login\n";
  }
}
请求命中原因
GET /docsA完全匹配最高
GET /docs/D不是完全匹配(有斜杠),D 前缀最长
GET /docs/api/avatar.pngB/docs/api 前缀匹配,^~ 优先级高于正则 C,所以 C 不生效
GET /static/logo.pngC没有前缀命中,走正则,.*png 匹配
GET /docs/api/loginBB 的 ^~ /docs/api 命中后不再继续,所以 E(更长的普通前缀)也不会生效

第二部分:proxy_pass 末尾斜杠的差异(最关键)

proxy_pass 后面跟的 URL 带不带 / 结尾,路径拼接方式完全不同。这是 Nginx 配置最容易踩的坑,没有之一。

记一句话:

  • proxy_pass URL 末尾带 / = 把当前 location 匹配到的那一段从请求路径里截掉,然后把剩下的拼到 proxy_pass URL 后面
  • proxy_pass URL 末尾不带 / = 把完整的请求路径直接拼到 proxy_pass URL 后面

下面用同一个 location /docs/api 做对比,假设请求是 GET /docs/api/user/me。

写法 1:proxy_pass 带末尾斜杠(截掉前缀)

location /docs/api {
  #                 注意这里末尾的 /
  proxy_pass http://127.0.0.1:3000/;
}

拼接过程:

  1. location 匹配到的前缀 /docs/api 被截掉 → 剩下 /user/me
  2. 拼到 proxy_pass URL http://127.0.0.1:3000/ 后面
  3. 后端实际收到:GET /user/me ✅

这是最常用的写法,相当于把 /docs/api 这一段「挂在」后端根路径 / 下。

写法 2:proxy_pass 不带斜杠(整段拼)

location /docs/api {
  #                 末尾没有 /
  proxy_pass http://127.0.0.1:3000;
}

拼接过程:

  1. 完整请求路径 /docs/api/user/me 直接拼
  2. 后端实际收到:GET /docs/api/user/me ⚠️

也就是说你的后端路由得真的有 /docs/api/user/me 这条,否则就是 404。

写法 3:proxy_pass 带子路径 + 末尾斜杠

location /docs/api {
  proxy_pass http://127.0.0.1:3000/internal/;
}

请求 /docs/api/user/me → 后端收到 GET /internal/user/me。

结论:只要 proxy_pass 的 URL 里 http://host:port 后面还带了任何路径(哪怕只有 /), Nginx 就会把 location 匹配到的前缀截掉再拼接。如果只写了 http://host:port 后面没有任何路径(不带斜杠),就会把原请求整条拼过去。


第三部分:完整可运行示例:Node.js 应用反代 + HTTPS

后端:node app.js 监听 127.0.0.1:3000,有两条路由 GET / 和 GET /health。 对外域名:docs.example.com,HTTPS 用 Let's Encrypt 的证书。

/etc/nginx/sites-available/docs.example.com.conf

# ------ 80 端口:永久跳 HTTPS
server {
    listen 80;
    listen [::]:80;
    server_name docs.example.com;

    # ACME 挑战路径不走跳转(certbot 自动续期需要)
    location /.well-known/acme-challenge/ {
        root /var/www/html;
    }

    location / {
        return 301 https://$host$request_uri;
    }
}

# ------ 443 端口:真正的服务
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name docs.example.com;

    # SSL 证书(certbot --nginx 会自动写)
    ssl_certificate     /etc/letsencrypt/live/docs.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/docs.example.com/privkey.pem;

    # 上传文件大小限制(默认 1M 不够)
    client_max_body_size 20m;

    # 超时(后端接口慢时别被 Nginx 先砍了)
    proxy_connect_timeout 10s;
    proxy_send_timeout    60s;
    proxy_read_timeout    60s;

    # ------ 健康检查:暴露 /health 给云厂商 LB
    location = /health {
        access_log off;
        proxy_pass http://127.0.0.1:3000/health;
    }

    # ------ 静态资源:本地文件不走反代
    location /static/ {
        alias /var/www/docs.example.com/static/;
        expires 7d;
        add_header Cache-Control "public, immutable";
    }

    # ------ 主反代:其他所有请求丢给 Node
    location / {
        # 末尾斜杠行为:location / 匹配空串,所以带不带 / 结果一样
        proxy_pass http://127.0.0.1:3000;

        # 这 5 行 header 基本必带,让后端知道真实的协议和用户 IP
        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_set_header X-Forwarded-Host  $host;
    }
}

完整 upstream 负载均衡示例(多台后端)

有 2 台 Node(127.0.0.1:3000 + 10.0.0.2:3000),默认轮询:

http {
    upstream docs_backend {
        server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
        server 10.0.0.2:3000   max_fails=3 fail_timeout=30s backup; # backup=备机,平时不用
        keepalive 32; # 长连接,避免每次反代都建 TCP 握手
    }

    server {
        # ...
        location / {
            proxy_pass http://docs_backend;
            proxy_http_version 1.1;       # keepalive 必须配这两行
            proxy_set_header Connection "";
            # ...其他 header
        }
    }
}

第四部分:502 Bad Gateway 排查 4 步(按顺序)

80% 的 502 都出在这 4 件事里,别一上来就怀疑 Nginx bug。

第 1 步:后端进程是不是真的在监听?

ss -ltnp | grep 3000
# 正常应该看到 LISTEN 状态,进程名 node / python 等
# 如果啥也没有 → 后端没起来 / 崩了 / 端口写错了

第 2 步:在服务器本机 curl 后端

curl -v http://127.0.0.1:3000/health

如果这里都 Connection refused / timeout → 后端没起来 / 挂了 / 绑错了 IP(比如后端 bind 了 192.168.x.x 而 Nginx 连的是 127.0.0.1)。

第 3 步:看 Nginx error log

tail -n 50 /var/log/nginx/error.log

常见错误字符串一眼就能读:

  • connect() failed (111: Connection refused) → 后端没起来(和第 2 步一致)
  • connect() failed (110: Connection timed out) → Nginx 到后端之间网络不通(比如安全组 / 防火墙 / 跨机 IP)
  • upstream prematurely closed connection → 后端处理到一半崩了,看后端自己的日志
  • no live upstreams while connecting to upstream → upstream 里所有机器全挂了

第 4 步:检查 SELinux(RHEL / CentOS / Rocky / AlmaLinux 系列)

Debian/Ubuntu 默认没开 SELinux 基本不会遇到。RHEL 系默认的 SELinux 会阻止 Nginx 连外网和非标准端口:

# 查看布尔值
getsebool httpd_can_network_connect
# 如果是 off,临时打开
setsebool -P httpd_can_network_connect 1

常见问题 FAQ

Q1. 我想让某一段路径 403 Forbidden(比如 .env 文件),怎么写?

location ~ /\.env {
    deny all;
    return 403;
}

Q2. 根路径 / 跳 /docs 用 301 还是 302?

SEO 相关的永久跳转用 301(比如站点永久搬迁),临时调试用 302。

location = / {
    return 301 /docs;
}

Q3. proxy_pass 后面的 upstream 名能不能带路径?

可以:proxy_pass http://docs_backend/v1/;,路径拼接逻辑和直接写 URL 一样,末尾斜杠的规则完全相同。


更正记录

  • 2026-08-09:首版发布。所有示例在 Nginx 1.26.2 上 nginx -t + curl 实跑通过。

写完命令直接跑?打开在线工具集

Base64 编解码、URL 编码、JSON 格式化、正则测试、时间戳转换、JWT 解析等 40+ 工具。 查文档时顺手验证,不用切站。

打开工具集

希望你们的产品文档也被 AI 引擎引用?

元析智能的 GEO(Generative Engine Optimization)服务:结论前置结构、Schema.org 标注、信源权重建设,让 ChatGPT / 豆包 / 秘塔 / Perplexity 等优先引用你的官方文档。

联系我们做 GEO 优化