把一个 HTTP 端口反代出来很简单,三行 proxy_pass 就能跑起来。但在真实的生产环境中,很快就会遇到各种实际问题:套了 Cloudflare CDN 之后后端日志全显示 CDN 节点的 IP,查不出真实访客;跑实时聊天、SSH Web 终端或智能群管机器人时,由于缺少必要的 Upgrade 请求头导致 WebSocket 频繁断连;或者升级到新版 Nginx 后旧的 http2 on; 语法疯狂报废弃警告。
这篇笔记记录生产环境中 Nginx 反向代理必配的几个核心配置项:WebSocket 双向代理、Cloudflare 真实客户端 IP 穿透、现代 HTTP/2 语法,以及一份开箱即用的配置模板。
生产反向代理常见的几个深水区坑点
基础反代只是简单的“端口转接”,而生产环境面临复杂的外部 CDN、长连接协同与网络合规:
【外界访客 (114.x.x.x)】
│
▼ 发起 HTTPS / WSS 请求
┌───────────────────────────────────────────────────────────┐
│ ☁️ Cloudflare 全球 Anycast CDN 节点 (172.64.x.x) │
└───────────────────────────────────────────────────────────┘
│
▼ 携带 CF-Connecting-IP 请求头穿透
┌───────────────────────────────────────────────────────────┐
│ 🏢 你的生产 Nginx 网关 │
│ │
│ 🔍 真实 IP 还原模块:识别 CF 官方 IP 段,剔除代理伪装, │
│ 将真正的客户端 IP (114.x.x.x) 还原并交由后端应用记录; │
│ ⚡ WebSocket 动态协议升级(map 机制拒绝死连); │
│ 🚀 HTTP/2 现代流式并发多路复用(采用 http2 on; 语法); │
│ 🛡️ 跨域 CORS 精准白名单校验。 │
└───────────────────────────────────────────────────────────┘
准备工作
- [ ] Nginx 已处于运行状态:执行
nginx -v建议为 1.20+ 或 1.24+ 较新版本; - [ ] 后端长连接或 API 服务就绪:跑在本地特定端口(如
127.0.0.1:8080); - [ ] 已理解草稿箱与生效箱机制:改动永远在
/etc/nginx/sites-available/进行。
配置 WebSocket 动态升级转发
很多教程教小白直接在 location 里写死:
proxy_set_header Connection "upgrade"; # ❌ 严重反模式!
- 🗣️ 死穴解析:如果客户端发来的是普通 HTTP 请求(并没有要求 Upgrade),你却强行告诉后端“要升成 WebSocket”,很多后端(如 Node.js、Python FastAPI、Go)会当场报协议解析错误!
✅ 官方工业级正解:利用 map 动态握手!
服务器端: 打开 /etc/nginx/nginx.conf,在 http { ... } 内部放入这段全局动态映射(如果系统没有的话):
# 检查是否已存在 map 规则,没有则自包含幂等写入
if ! grep -q "connection_upgrade" /etc/nginx/nginx.conf 2>/dev/null; then
sed -i '/http {/a \ map $http_upgrade $connection_upgrade {\n default upgrade;\n "" close;\n }' /etc/nginx/nginx.conf
echo "[✓] 全局 WebSocket 动态映射已写入!"
fi
- 🗣️ 大白话:当访客发来 WebSocket 升级头时,Connection 设为
upgrade;如果是普通网页请求,Connection 设为close。聪明自适应,永远不报错!
还原真实访客 IP(穿透 Cloudflare CDN)
当你套了 Cloudflare CDN 后,如果没有做还原,你的所有后端访问日志、防刷限流、风控系统里记录的全是 Cloudflare 节点的机房 IP(如 172.64.x.x 或 104.28.x.x),导致 Fail2ban 误封 CDN。
我们需要告诉 Nginx:这些 IP 属于受信代理,真实 IP 放在 CF-Connecting-IP 报头里!
服务器端: 一键写入 Cloudflare 官方 IPv4/IPv6 受信网段配置:
cat << 'EOF' > /etc/nginx/conf.d/cloudflare-real-ip.conf
# Cloudflare 官方 IPv4 网段
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;
# Cloudflare 官方 IPv6 网段
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;
# 指定通过 CF 官方头还原真实访客 IP
real_ip_header CF-Connecting-IP;
EOF
- ✅ 成功标志:此配置落地后,你的
$remote_addr将奇迹般地变回访客手机或家庭宽带的真实 IP,防爆破与访问统计瞬间恢复精准!
适配新版 Nginx 的 HTTP/2 监听语法
在老教程里,很多教程写:
listen 443 ssl http2; # ❌ Nginx 1.25.1+ 已正式废弃此语法!
如果你在新版系统(Debian 12+ 或 Ubuntu 24.04)上这么写,运行 nginx -t 会疯狂刷黄字:
nginx: [warn] the "http2" directive is deprecated, use the "http2" directive instead。
✅ 现代化标准的 HTTP/2 写法:
listen 443 ssl;
listen [::]:443 ssl;
http2 on; # 🟢 官方规范新语法,清爽独立!
开箱即用的完整反代站点模板
现在我们将上面的所有生产特性(真实 IP 还原、动态 WebSocket、HTTP/2、大文件放行、超时保护)融为一体,生成一份真正无可挑剔的站点配置。
服务器端: 先填写你的配置变量:
MY_DOMAIN="api.0000996.xyz"
UPSTREAM_PORT="8080"
接着,整段执行自包含防呆写入脚本:
cat << EOF > /etc/nginx/sites-available/${MY_DOMAIN}.conf
server {
listen 80;
listen [::]:80;
server_name ${MY_DOMAIN};
# 生产安全:放行 100MB 附件与大文件上传
client_max_body_size 100M;
client_body_buffer_size 128k;
location / {
proxy_pass http://127.0.0.1:${UPSTREAM_PORT};
# 核心请求头透传
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;
# 动态 WebSocket 升级(依赖第 1 步的 map 规则)
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection \$connection_upgrade;
# 生产级超时与缓冲调优(防止长连接被提前中断)
proxy_connect_timeout 60s;
proxy_send_timeout 300s;
proxy_read_timeout 300s;
# 开启缓冲支持(高并发吞吐优化)
proxy_buffering on;
proxy_buffers 16 32k;
proxy_busy_buffers_size 64k;
}
}
EOF
# 激活站点软链接
ln -sf /etc/nginx/sites-available/${MY_DOMAIN}.conf /etc/nginx/sites-enabled/
# 黄金自检
nginx -t && systemctl reload nginx
- ✅ 成功标志:终端打印出
syntax is ok与test is successful,服务平滑重载完毕!
常见报错排查
| 异常现场 | 真实根因 | 对应一秒救命招式 |
|---|---|---|
nginx -t 报 unknown "connection_upgrade" variable |
漏掉了第 1 步在 nginx.conf 里的 map 定义 |
重新执行第 1 步脚本,确保 map 规则位于 http { ... } 内部。 |
反向代理访问报 504 Gateway Time-out |
后端处理耗时超过了默认的 60 秒限制 | 将配置里的 proxy_read_timeout 调大至 300s 或 600s,重载生效。 |
| 套了 Cloudflare 后日志仍显示国外 IP | 未安装 http_realip_module 或未配置 real_ip_header |
执行 nginx -V 2>&1 | grep realip 确认模块存在,并核实第 2 步的网段配置文件已加载。 |
浏览器控制台报 CORS header missing 跨域错误 |
前后端域名不同,未配置允许跨域标头 | 在 location 下加入 add_header Access-Control-Allow-Origin *;(生产建议填具体主域)。 |
还没有人在这篇手记留下纸条,来做第一个亮灯的人吧。