3.Nginx 反向代理与负载均衡

2026年5月7日 Nginx 15 分钟阅读 1 次阅读
📖 文章摘要

详解 Nginx 反向代理 proxy_pass 斜杠差异、真实 IP 透传配置,讲解 upstream 负载均衡策略,附带完整线上项目实战配置与排障问答。

Nginx 反向代理与负载均衡

一、反向代理核心概念

什么是反向代理

浏览器发请求给 Nginx,Nginx 把请求转发给后端服务器,后端返回的结果再原样返回给浏览器。对浏览器来说,它只知道 Nginx,不知道后面有 FastAPI、Nuxt 等后端服务在跑。Nginx 充当"中间人"角色。

为什么需要反向代理

  • 一个 Nginx 托管多个网站(通过 server_name 区分)
  • HTTPS 终止(浏览器和 Nginx 之间加密,Nginx 和后端之间可以明文)
  • 负载均衡(同一网站多个后端实例分担压力)
  • 安全隔离(后端不直接暴露给外网)

二、proxy_pass 斜杠问题(核心重点)

两种写法

# 写法A:proxy_pass 后面不带斜杠
location /api/ {
    proxy_pass http://127.0.0.1:8001;
}

# 写法B:proxy_pass 后面带斜杠
location /api/ {
    proxy_pass http://127.0.0.1:8001/;
}

区别(用户访问 /api/articles 时)

写法 转发目标 原因
不带斜杠(写法A) http://127.0.0.1:8001/api/articles location 路径原样拼接
带斜杠(写法B) http://127.0.0.1:8001/articles location 匹配的 /api/ 被截掉替换

规律

  • 不带斜杠 → 原样拼接(/api/ + articles = /api/articles
  • 带斜杠 → 截掉 location 匹配的部分,替换为 proxy_pass 后面的路径

为什么重要

博客用的是不带斜杠的写法:proxy_pass http://127.0.0.1:8001;
所以 FastAPI 后端的路由前缀必须写 /api/articles

如果用了带斜杠写法,后端路由就得改成 /articles(没有 /api 前缀)了。两种写法都可以,但后端代码要配套。


三、proxy_set_header 请求头传递

为什么需要

Nginx 反向代理后,后端看到的请求来源全部是 127.0.0.1(Nginx 本机)。后端需要知道用户真实信息,必须通过 proxy_set_header 传递。

逐行解释

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;
指令 作用 不传会怎样
Host $host 告诉后端原始域名是 demo.test.com 后端收到的是 127.0.0.1,可能导致域名路由错误
X-Real-IP $remote_addr 告诉后端真实用户 IP 后端日志里所有用户都是 127.0.0.1
X-Forwarded-For $proxy_add_x_forwarded_for 用户 IP(支持多级代理链) 同上,限流功能失效
X-Forwarded-Proto $scheme 告诉后端原始请求是 http 还是 https 后端不知道用户走的什么协议,可能生成错误的跳转链接

X-Forwarded-For 详细说明

这个头记录的是请求经过的所有代理 IP,从离用户最近的开始:

用户(shturl.cc/kvuLa) → CDN(10.0.0.1) → Nginx(10.0.0.2) → 后端(127.0.0.1)

后端收到的 X-Forwarded-For 值:shturl.cc/kvuLa, 10.0.0.1

$proxy_add_x_forwarded_for 这个变量会自动把前面代理传过来的值拼接到后面,所以多级代理链也能正确记录用户真实 IP。


四、负载均衡 upstream

基本配置

当博客流量大了,可以跑多个后端实例:

upstream blog_api {
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
}

server {
    location /api/ {
        proxy_pass http://blog_api;    # 指向 upstream 名字,不是直接写 IP
    }
}

三种负载均衡算法

1. 轮询(默认,不用写算法配置)

upstream blog_api {
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
}

请求依次分发:第1个给8001,第2个给8002,第3个给8001...适合一般场景,最常用。

2. least_conn(最少连接数)

upstream blog_api {
    least_conn;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
}

哪个后端当前连接数最少,请求就给谁。适合某个请求处理时间特别长的场景(比如大文件上传、长时间计算)。

3. ip_hash(同一IP始终同一后端)

upstream blog_api {
    ip_hash;
    server 127.0.0.1:8001;
    server 127.0.0.1:8002;
}

对用户 IP 做哈希,同一个 IP 永远访问同一个后端。适合需要 session 保持的场景(比如登录状态)。缺点是如果某个用户流量特别大,会把单个后端压垮。

upstream 参数详解

参数 作用 示例
weight=N 权重,数字越大分到的请求越多 weight=7 占 70%,weight=3 占 30%
backup 备用服务器,所有正常 server 挂了才启用 正常不接收流量,只做热备
max_fails=N 最大失败次数(在 fail_timeout 时间窗口内) 默认是 1
fail_timeout=Ns 失败计数窗口 + 暂停时间 超过后标记为不可用,等 N 秒再重试
down 手动标记该 server 下线,不再分配流量 维护时使用

参数组合效果

max_fails + fail_timeout 联合:
15秒内连续失败3次 → 该 server 被标记为不可用 → 过15秒后重新尝试 → 如果恢复就继续分配,没恢复就继续暂停。

weight 计算方法:
所有 server 的 weight 之和为总权重,每个 server 承担的流量比例 = 它的 weight / 总 weight。
例:weight=7 和 weight=3,总权重 10,7/10=70%,3/10=30%。

实战练习题

场景:3台服务器集群,A(10.1) 性能强承担70%流量,B(10.2) 性能一般承担30%,C(10.3) 仅作热备。A和B在15秒内3次连接失败则暂停分配。

upstream web_cluster {
    server 192.168.10.1 weight=7 max_fails=3 fail_timeout=15;
    server 192.168.10.2 weight=3 max_fails=3 fail_timeout=15;
    server 192.168.10.3 backup;
}

逐行解析:

  • weight=7 / weight=3 → 按 7:3 分配流量(70%:30%)
  • max_fails=3 → 15秒内最多允许失败3次
  • fail_timeout=15 → 失败窗口15秒,超过后暂停15秒再重试
  • backup → A和B全挂了才启用C

五、博客的完整反向代理配置

# /etc/nginx/conf.d/demo.conf

server {
    server_name demo.test.com www.demo.test.com;
    listen 443 ssl;
    ssl_certificate /etc/letsencrypt/live/demo.test.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/demo.test.com/privkey.pem;
    include /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;

    # API 请求 → FastAPI 后端
    location /api/ {
        proxy_pass http://127.0.0.1:8001;
        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;
    }

    # 静态文件直接服务(不经过后端)
    location /uploads/ {
        alias /root/blog/data/uploads/;
    }

    # 站点地图 → FastAPI
    location = /sitemap.xml {
        proxy_pass http://127.0.0.1:8001/api/sitemap.xml;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    # 百度验证文件
    location ~ ^/baidu_verify_ {
        root /root/blog/frontend/.output/public;
    }

    # 其他所有请求 → Nuxt 前端
    location / {
        proxy_pass http://127.0.0.1:3000;
        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;
    }
}

请求流程图

用户访问 https://demo.test.com/api/articles

浏览器 → Nginx(443)
         ├── location /api/ → proxy_pass http://127.0.0.1:8001
         │   → FastAPI 收到 /api/articles → 查数据库 → 返回 JSON
         │   → 原路返回给浏览器

用户访问 https://demo.test.com/about

浏览器 → Nginx(443)
         ├── location / → proxy_pass http://127.0.0.1:3000
         │   → Nuxt SSR 渲染 → 返回 HTML
         │   → 原路返回给浏览器

用户访问 https://demo.test.com/uploads/image.jpg

浏览器 → Nginx(443)
         ├── location /uploads/ → alias /root/blog/data/uploads/
         │   → 直接读取磁盘文件返回

六、问答

Q:反向代理是什么?
Nginx 接收客户端请求,转发给后端服务器,再把响应返回给客户端。客户端不知道后端的存在。

Q:proxy_pass 带斜杠和不带斜杠的区别?
不带斜杠:原样拼接 location 路径。带斜杠:截掉 location 匹配的部分。举个例子:访问 /api/articles,不带斜杠转发到 /api/articles,带斜杠转发到 /articles。

Q:为什么要传 Host、X-Real-IP、X-Forwarded-For?
Nginx 反向代理后,后端只看到 127.0.0.1。需要这些头来告诉后端原始域名、用户真实 IP、使用的协议。

Q:负载均衡有几种方式?
轮询(默认)、least_conn(最少连接)、ip_hash(IP哈希保持会话)。

Q:502 Bad Gateway 是什么问题?
Nginx 转发请求后,后端没有正常响应。先 curl 后端端口看通不通,再看后端进程是否活着。

说明:文中域名、IP均为教学演示虚拟地址,无真实线上服务

最后更新:2026年7月8日CC BY-NC-SA 4.0

评论

暂无评论,来写第一条吧

© 2026 My Blog. Built with Nuxt.js + FastAPI.