强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Nginx 从入门到精通 / 05 - 反向代理 / Reverse Proxy

反向代理 / Reverse Proxy

🟢 基础 / Basics — 最简单的反向代理

什么是反向代理?

正向代理(Forward Proxy):客户端知道目标服务器,代理代表客户端
Client ──► Proxy ──► Target Server
         (代理访问外部资源,如翻墙)

反向代理(Reverse Proxy):客户端不知道真实服务器,代理代表服务器
Client ──► Nginx ──► Backend Server
         (客户端以为 Nginx 就是服务器)

基本配置

server {
    listen 80;
    server_name example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;   # 转发到本地 3000 端口
    }
}

就这么简单。客户端访问 example.com,Nginx 将请求转发到运行在 3000 端口的应用。

proxy_pass 带 URI vs 不带 URI

这是反向代理最容易踩的坑:

# 情况 1:不带 URI(推荐)
location /api/ {
    proxy_pass http://127.0.0.1:3000;
    # 请求 /api/users → 转发 http://127.0.0.1:3000/api/users
    # 原始 URI 完整传递
}

# 情况 2:带 URI(带尾部斜杠)
location /api/ {
    proxy_pass http://127.0.0.1:3000/;
    # 请求 /api/users → 转发 http://127.0.0.1:3000/users
    # /api/ 被替换为 /
}

# 情况 3:带 URI(不带尾部斜杠)
location /api/ {
    proxy_pass http://127.0.0.1:3000/v2;
    # 请求 /api/users → 转发 http://127.0.0.1:3000/v2users
    # ⚠️ 注意:/api/ 被替换为 /v2,没有斜杠!
}

记忆表 / URI Mapping Table:

locationproxy_pass请求 /api/users → 实际转发
/api/http://backendhttp://backend/api/users
/api/http://backend/http://backend/users
/api/http://backend/v2http://backend/v2users ⚠️
/api/http://backend/v2/http://backend/v2/users

规则: 如果 proxy_pass 带 URI(包括 /),则 location 匹配的部分会被替换。

必须设置的 Header

location / {
    proxy_pass http://127.0.0.1:3000;

    # 1. 传递真实客户端 IP
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    # 2. 传递原始 Host
    proxy_set_header Host $host;

    # 3. 传递协议信息
    proxy_set_header X-Forwarded-Proto $scheme;

    # 4. 启用 HTTP/1.1(支持 keepalive)
    proxy_http_version 1.1;
    proxy_set_header Connection "";
}

为什么这些 Header 重要?

没有 proxy_set_header X-Real-IP 时:
后端看到的客户端 IP = 127.0.0.1(Nginx 的 IP)
→ 无法做 IP 限流、IP 白名单、地理位置

设置后:
后端看到的客户端 IP = 203.0.113.50(真实客户端 IP)
→ 可以正确做 IP 相关的逻辑

🟡 进阶 / Intermediate — 生产环境反向代理

完整的反向代理配置

upstream backend {
    server 127.0.0.1:3000;
    server 127.0.0.1:3001;
    keepalive 32;            # 到后端的连接池
}

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

    # SSL 配置(后续章节详解)
    ssl_certificate     /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # 代理缓冲(避免慢客户端阻塞后端)
    proxy_buffering on;
    proxy_buffer_size 4k;
    proxy_buffers 8 16k;
    proxy_busy_buffers_size 32k;

    # 超时设置
    proxy_connect_timeout 5s;      # 连接后端的超时
    proxy_send_timeout 30s;        # 发送请求到后端的超时
    proxy_read_timeout 30s;        # 从后端读取响应的超时

    # 错误处理:后端故障时尝试下一个
    proxy_next_upstream error timeout http_502 http_503;
    proxy_next_upstream_tries 3;
    proxy_next_upstream_timeout 10s;

    location / {
        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_http_version 1.1;
        proxy_set_header Connection "";
    }
}

WebSocket 代理

# WebSocket 需要特殊的 header 处理
location /ws/ {
    proxy_pass http://127.0.0.1:3000;

    # WebSocket 必需的三个 header
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";

    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;

    # WebSocket 超时(默认 60s 会断开)
    proxy_read_timeout 3600s;     # 1 小时
    proxy_send_timeout 3600s;
}
WebSocket 握手流程:

Client → Nginx → Backend
GET /ws/ HTTP/1.1
Upgrade: websocket
Connection: Upgrade

Client ← Nginx ← Backend
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade

Client ⟷ Nginx ⟷ Backend
WebSocket 双向通信(长连接)

HTTP/2 代理

server {
    listen 443 ssl http2;    # 客户端到 Nginx:HTTP/2

    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;    # Nginx 到后端:HTTP/1.1
        # ⚠️ 大部分后端不支持 h2c(明文 HTTP/2)
        # Nginx 到后端通常用 HTTP/1.1
    }
}

# 如果后端支持 h2c(如 gRPC 服务)
# location /grpc/ {
#     grpc_pass grpc://backend;
#     # 或
#     proxy_pass http://backend;
#     grpc_set_header ...;
# }

gRPC 代理

upstream grpc_backend {
    server 127.0.0.1:50051;
}

server {
    listen 443 ssl http2;
    server_name grpc.example.com;

    ssl_certificate     /etc/ssl/certs/grpc.example.com.pem;
    ssl_certificate_key /etc/ssl/private/grpc.example.com.key;

    location /package.ServiceName/ {
        grpc_pass grpc://grpc_backend;

        # gRPC 错误处理
        error_page 502 = @grpc_error;
        error_page 503 = @grpc_error;
        error_page 504 = @grpc_error;
    }

    location @grpc_error {
        default_type application/grpc;
        add_header grpc-status 14;              # UNAVAILABLE
        add_header grpc-message "Service unavailable";
        return 204;
    }
}

多后端协议代理

server {
    server_name example.com;

    # REST API
    location /api/ {
        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }

    # WebSocket
    location /ws/ {
        proxy_pass http://127.0.0.1:3001;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 3600s;
    }

    # gRPC
    location /grpc/ {
        grpc_pass grpc://127.0.0.1:50051;
    }

    # 静态文件(Nginx 直接处理)
    location /static/ {
        alias /var/www/static/;
        expires 30d;
    }

    # 前端 SPA
    location / {
        root /var/www/spa;
        try_files $uri $uri/ /index.html;
    }
}

🔴 高级 / Advanced — 高级代理技巧

proxy_buffering 详解

无 buffering(proxy_buffering off):
Client ←─── Nginx ←─── Backend
         逐字节转发,后端被慢客户端阻塞

有 buffering(proxy_buffering on,默认):
Client ←─── Nginx ←─── Backend
         Nginx 缓冲响应,后端快速释放
Client ← Nginx(从缓冲区慢慢发给客户端)
location / {
    proxy_pass http://backend;
    proxy_buffering on;

    # 缓冲区配置
    proxy_buffer_size 4k;          # 第一部分(响应头)的缓冲区
    proxy_buffers 8 16k;           # 响应体缓冲区(8 个 16KB)
    proxy_busy_buffers_size 32k;   # 在客户端接收时可以发送的最大缓冲区

    # 临时文件(响应太大放不进缓冲区时)
    proxy_temp_path /var/cache/nginx/temp;
    proxy_max_temp_file_size 1024m;
}

大文件上传代理

location /upload/ {
    proxy_pass http://127.0.0.1:3000;

    # 允许大文件上传
    client_max_body_size 500m;

    # 关闭请求体缓冲,直接转发给后端
    proxy_request_buffering off;

    # 超时调整(大文件上传耗时长)
    proxy_connect_timeout 10s;
    proxy_send_timeout 300s;
    proxy_read_timeout 300s;
}

代理缓存穿透防护

# 当后端返回错误时,使用过期缓存兜底
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_cache_background_update on;   # 后台更新缓存,用户不等待
proxy_cache_lock on;                # 防止缓存击穿(同时只放一个请求穿透)
proxy_cache_lock_timeout 5s;

自定义错误页面

# 方式 1:Nginx 自己的错误页面
error_page 502 503 504 /50x.html;
location = /50x.html {
    root /var/www/error-pages;
    internal;
}

# 方式 2:后端错误时显示自定义页面
proxy_intercept_errors on;    # 拦截后端返回的错误状态码
error_page 502 503 504 /maintenance.html;
location = /maintenance.html {
    root /var/www/error-pages;
    internal;
}

多层代理 Header 传递

真实客户端 → CDN → Nginx → 应用

# CDN 会添加 X-Forwarded-For,但 Nginx 需要信任 CDN 的 header
# 否则后端只能看到 CDN 的 IP

# 使用 realip 模块提取真实 IP
set_real_ip_from  103.21.244.0/22;    # Cloudflare IP 段
set_real_ip_from  103.22.200.0/22;
set_real_ip_from  173.245.48.0/20;
real_ip_header    CF-Connecting-IP;    # Cloudflare 的真实 IP header
# 或
real_ip_header    X-Forwarded-For;
real_ip_recursive on;                  # 递归剥离可信代理 IP

超时与重试策略

location /api/ {
    proxy_pass http://backend;

    # 三级超时
    proxy_connect_timeout 5s;     # 连接建立:5 秒
    proxy_send_timeout 10s;       # 发送请求体:10 秒
    proxy_read_timeout 30s;       # 等待响应:30 秒

    # 重试策略
    proxy_next_upstream error timeout http_502 http_503 http_504;
    proxy_next_upstream_tries 2;           # 最多重试 2 次
    proxy_next_upstream_timeout 10s;       # 重试总超时 10 秒
    proxy_next_upstream_status http_502 http_503;  # 哪些状态码触发重试

    # ⚠️ 非幂等请求(POST/PUT)不要重试!
    # 可以用 map 区分:
    # proxy_next_upstream $upstream_retry;
}
# 根据请求方法决定是否重试
map $request_method $upstream_retry {
    GET     "error timeout http_502 http_503";
    HEAD    "error timeout http_502 http_503";
    default "";    # POST/PUT/DELETE 不重试
}

小结 / Summary

层级你需要知道的 / What You Need to Know
🟢 基础proxy_pass,Header 传递(X-Real-IP, Host, X-Forwarded-For)
🟡 进阶WebSocket 代理,gRPC 代理,缓冲配置,多后端协议
🔴 高级大文件上传,缓存穿透防护,realip 模块,超时重试策略

下一章:负载均衡 / Load Balancing