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:
| location | proxy_pass | 请求 /api/users → 实际转发 |
|---|---|---|
/api/ | http://backend | http://backend/api/users |
/api/ | http://backend/ | http://backend/users |
/api/ | http://backend/v2 | http://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 模块,超时重试策略 |