Nginx 反向代理进阶实战:WebSocket 代理、负载均衡与常见排错


<!DOCTYPE html>

Nginx 反向代理进阶实战:WebSocket 代理、负载均衡与常见排错

反向代理是 Nginx 最核心的能力:对外统一入口、对内分发请求。很多同学会配一个最简单的 proxy_pass,但一旦遇到 WebSocket 连接闪断、多后端负载不均、TCP 四层转发、502/504 报错,就不知道从何下手。本文按「HTTP 反代 → 负载均衡 → WebSocket → 四层代理 → 排错」的顺序进阶,全部配置可直接复制修改使用。

一、反向代理基础:别漏了转发头

最基础的反向代理,把 80 端口的请求转发给本机 8080 的 Java/Go 应用:

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        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;
    }
}

三个必须懂的细节:

# 1. proxy_pass 末尾【不带】路径 vs【带】路径,行为完全不同
proxy_pass http://127.0.0.1:8080;          # 保留原始 URI:/api/user → /api/user
proxy_pass http://127.0.0.1:8080/;         # 替换 URI:/api/user → /user(常用于前后端分离)
proxy_pass http://127.0.0.1:8080/v2;       # 前缀替换:/api/user → /v2/user

# 2. 后端拿到的是 Nginx 的地址,不是用户地址!必须用 X-Real-IP 传递真实 IP
# 3. 应用要开启对 X-Forwarded-For 的信任,否则获取的客户端 IP 仍是内网地址
最容易踩坑:location /api/ { proxy_pass http://backend; } 时,如果后端路由是 /api/xxx,必须保证 proxy_pass 不带尾部路径;反之带尾路径会把 /api 前缀剥掉。前后端联调出现 404 时,90% 是这里的问题。

二、负载均衡:upstream 的正确打开方式

多台后端时,用 upstream 定义后端组:

upstream backend_servers {
    # 默认轮询(round-robin),按请求数轮流分发
    server 10.0.0.11:8080 weight=3;      # 权重 3:更强机器分更多流量
    server 10.0.0.12:8080 weight=1;
    server 10.0.0.13:8080 max_fails=3 fail_timeout=30s;  # 30 秒内失败 3 次则摘除
    keepalive 32;                        # 长连接池,减少后端建连开销
}

server {
    listen 80;
    server_name app.example.com;

    location / {
        proxy_pass http://backend_servers;
        proxy_http_version 1.1;          # keepalive 必须配 1.1
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_next_upstream error timeout http_502 http_503;  # 故障自动切换
    }
}

四种负载均衡策略对比:

策略配置写法适用场景
轮询默认后端性能相近
加权轮询server IP weight=3后端性能有差异
IP 哈希upstream 内加 ip_hash;需要会话保持(无共享 session)
最少连接upstream 内加 least_conn;请求耗时差异大、长连接多
# 会话保持示例(基于客户端 IP 哈希)
upstream session_backend {
    ip_hash;
    server 10.0.0.11:8080;
    server 10.0.0.12:8080;
}

三、WebSocket 反向代理:解决「连上就断」

WebSocket 建连时先发 HTTP 请求,携带 Upgrade: websocket 和 Connection: Upgrade 头。Nginx 默认不会转发这两个头,必须显式配置:

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

upstream ws_backend {
    server 127.0.0.1:9501;      # 如 Swoole / Node.js WebSocket 服务
}

server {
    listen 80;
    server_name ws.example.com;

    location /ws {
        proxy_pass http://ws_backend;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;

        # WebSocket 是长连接,必须调大超时,否则 60s 后自动断开
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

排错要点:

# 1. 忘配 Upgrade/Connection 头 → 客户端报 400 / 连接立即关闭
# 2. 忘了调 proxy_read_timeout → 连接在默认 60s 被 Nginx 掐断
# 3. 前端 wss 域名证书过期 → 浏览器控制台报 SSL 错误,与 Nginx 无关
# 4. 验证后端真实连接数(排除 Nginx 层问题)
ss -tlnp | grep 9501

四、四层代理:stream 模块转发 TCP/UDP

除了 HTTP,Nginx 还能转发 MySQL、Redis、SSH 等 TCP 流量,靠 stream 模块:

# 确认编译了 stream 模块
nginx -V 2>&1 | grep -- --with-stream

# 在 nginx.conf 顶部加载
stream {
    upstream mysql_group {
        server 10.0.0.21:3306;
        server 10.0.0.22:3306;
    }

    server {
        listen 3306;
        proxy_pass mysql_group;
        proxy_timeout 300s;
    }
}

注意:stream 和 http 是平级块,不能放在 http 块内。Debian/Ubuntu 下需要把 stream 配置独立到 /etc/nginx/conf.d/stream.conf 或使用 nginx-extras 包。

# 测试连接(客户端视角)
mysql -h 你的NginxIP -P 3306 -u root -p
# 抓包确认四层转发
tcpdump -i eth0 port 3306 -nn

五、HTTPS 与后端加密回源

前端 HTTPS、后端 HTTP 是最常见组合;后端也是 HTTPS 时需要额外处理证书校验:

server {
    listen 443 ssl;
    server_name app.example.com;

    ssl_certificate     /etc/nginx/ssl/example.com.pem;
    ssl_certificate_key /etc/nginx/ssl/example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;

    location / {
        proxy_pass https://backend_servers;   # 后端走 HTTPS

        # 后端证书是自签/不匹配时,关闭校验(仅内网可信环境)
        proxy_ssl_verify off;
        # 多域名后端时需要 SNI
        proxy_ssl_server_name on;
        proxy_ssl_name backend.internal.example.com;

        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

六、高频排错:502 / 504 / 404

502 Bad Gateway —— 上游连不上

# 1. 后端真的没起来?
ss -tlnp | grep 8080
systemctl status 你的应用

# 2. 后端起来了但 socket 文件路径不对(PHP-FPM 常见)
#    location ~ \.php$ { fastcgi_pass unix:/run/php/php8.2-fpm.sock; }
ls -l /run/php/php8.2-fpm.sock

# 3. 后端起在 127.0.0.1 上,但 Nginx 配的是内网 IP
ss -tlnp | grep 8080   # 确认 Listen 地址与 proxy_pass 一致

# 4. 看 Nginx 错误日志(最有价值的信息源)
tail -f /var/log/nginx/error.log

504 Gateway Timeout —— 上游响应太慢

# 默认 60 秒超时,大数据量接口/慢 SQL 会触发
proxy_connect_timeout 10s;    # 建立连接超时
proxy_send_timeout 60s;       # 发送请求超时
proxy_read_timeout 120s;      # 读取响应超时(最常需要调大)

# 如果后端本身 5 分钟才能出结果,要同时检查后端自身超时设置
# 例:PHP-FPM 的 request_terminate_timeout、Python 的请求超时

404 —— 多半是 proxy_pass 路径问题

# 现象:直接访问后端正常,走 Nginx 就 404
# 排查:把 location 与 proxy_pass 的路径关系画出来
location /api/ { proxy_pass http://backend:8080; }      # /api/user → /api/user ✅
location /api/ { proxy_pass http://backend:8080/; }     # /api/user → /user   可能404
location /api/ { proxy_pass http://backend:8080/v1; }   # /api/user → /v1user  路径拼接错误 ❌

# 规律:proxy_pass 不带路径 → URI 原样转发;带路径 → 前缀替换
# 想替换前缀并保留后缀,写法:proxy_pass http://backend:8080/v1/;  (注意结尾斜杠)

七、性能与安全建议

# 1. 打开 upstream 长连接池(http 1.1 + keepalive)
proxy_http_version 1.1;
proxy_set_header Connection "";
upstream 内 keepalive 32;

# 2. 限制单 IP 并发,防被刷
limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
location /api/ {
    limit_req zone=api burst=20 nodelay;
    proxy_pass http://backend_servers;
}

# 3. 隐藏后端真实信息
proxy_hide_header X-Powered-By;
server_tokens off;

# 4. 大文件上传时调大 client_max_body_size(默认仅 1M!)
client_max_body_size 100m;

# 5. 健康检查:后端故障时快速摘除
upstream backend_servers {
    server 10.0.0.11:8080 max_fails=2 fail_timeout=10s;
    server 10.0.0.12:8080 max_fails=2 fail_timeout=10s;
}
# 修改配置后务必先测语法再重载
nginx -t && nginx -s reload

八、总结

反向代理进阶的核心就三件事:转发头传对(真实 IP、协议、URI)、超时与连接池调好(WebSocket、慢接口、长连接)、排错按日志走(error.log + ss 双确认)。记住一条铁律:改完配置永远先 nginx -t 再 reload。把本文的负载均衡、WebSocket、四层代理三段配置按需组合,一个高可用的网关就成型了。

如果你在实战中遇到 WebSocket 频繁断开、负载不均或奇怪的 5xx,欢迎评论区留言,带上你的 server 配置片段一起分析。


手机扫码阅读

微信或手机浏览器扫一扫,随时随地随心阅读与分享

frp 内网穿透实战:服务端配置、客户端连接与 HTTPS 域名穿透全指南

Prometheus + Grafana 监控搭建实战:Node Exporter 采集与告警规则全流程

评 论
更换验证码