教程 · 建站与 Web
用 Nginx 反向代理后端应用(含 WebSocket 与真实 IP)
难度
进阶
预计时长
20 分钟
步骤
7 步
最近修订
基准系统
Debian 12
要点
用 Nginx 反向代理后端应用(含 WebSocket 与真实 IP)
反向代理让 Nginx 在 80/443 上接收请求,再转发给只监听 127.0.0.1 的后端应用。关键是四个 proxy_set_header:Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto,WebSocket 还需要 Upgrade 与 Connection 两个头。后端只绑回环地址,公网就无法绕过代理直连。
前置条件
开始之前,先确认这些都具备
- 一个已经在本机某端口跑起来的后端应用(Node.js、Python、Go 均可)
- 已安装 Nginx,并且域名已解析到本机
操作步骤
共 7 步
步骤 01 / 07
先确认后端只监听回环地址 #
这是反向代理最容易被忽略的安全前提:如果应用监听 0.0.0.0:3000,那么公网可以直接访问 3000 端口,你在 Nginx 上做的限速、认证、日志全部被绕过。ss 的输出里应当看到 127.0.0.1:3000 而不是 *:3000。
Shell sudo ss -tlnp | grep 3000 # 期望:127.0.0.1:3000 # 如果是 0.0.0.0:3000,去改应用的监听地址,而不是靠防火墙补救步骤 02 / 07
为 WebSocket 准备一个 map #
Connection 头不能写死成 upgrade,否则普通 HTTP 请求也会带上这个头,部分后端会因此报错。用 map 根据请求是否携带 Upgrade 来决定取值,这是官方文档给出的标准写法。map 必须放在 http 上下文里,所以单独放一个文件。
Nginx 配置 # /etc/nginx/conf.d/websocket-upgrade.conf map $http_upgrade $connection_upgrade { default upgrade; '' close; }步骤 03 / 07
写代理 server 块 #
四个转发头缺一不可:Host 让后端知道原始域名,X-Real-IP 与 X-Forwarded-For 让日志里不再全是 127.0.0.1,X-Forwarded-Proto 让后端知道外面走的是 HTTPS —— 少了最后一个,很多框架会生成 http 开头的绝对链接,在浏览器里表现为无限重定向。
Nginx 配置 # /etc/nginx/sites-available/app.example.com server { listen 80; server_name app.example.com; client_max_body_size 32m; location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; 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_set_header Upgrade $http_upgrade; proxy_set_header Connection $connection_upgrade; proxy_connect_timeout 5s; proxy_read_timeout 300s; } }步骤 04 / 07
启用并验证 #
先用本机 curl 带 Host 头测试,把「代理配置」与「DNS/防火墙」两类问题分开。502 通常意味着后端没起来或端口写错,504 意味着后端太慢。
Shell sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/ sudo nginx -t && sudo systemctl reload nginx curl -I -H "Host: app.example.com" http://127.0.0.1 sudo tail -f /var/log/nginx/error.log步骤 05 / 07
加上 HTTPS #
和静态站点一样用 certbot 签发;插件会把 443 监听与证书行写进同一个 server 块。上线之后记得确认后端收到的 X-Forwarded-Proto 是 https。
Shell sudo certbot --nginx -d app.example.com --agree-tos -m [email protected] --no-eff-email --redirect curl -sI https://app.example.com | head -n 1步骤 06 / 07
如果前面还有一层 CDN,需要还原真实 IP #
套了 CDN 之后,Nginx 看到的 $remote_addr 是 CDN 边缘节点的地址。用 real_ip 模块把它还原成真实访客 IP,但 set_real_ip_from 必须逐段写明 CDN 官方公布的 IP 段 —— 写成 0.0.0.0/0 等于允许任何人伪造自己的来源 IP,把限速和封禁全部架空。
Nginx 配置 # /etc/nginx/conf.d/realip.conf # 下面的网段请从 CDN 官方公布的列表逐段填写,并定期更新 set_real_ip_from 203.0.113.0/24; real_ip_header CF-Connecting-IP; real_ip_recursive on;步骤 07 / 07
长连接与大文件的两处调整 #
服务端推送(SSE)和流式响应需要关掉缓冲,否则消息会被 Nginx 攒着不发;上传大文件要调高 client_max_body_size,默认 1 MB,超过就是 413。
Nginx 配置 location /events { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Connection ''; proxy_buffering off; proxy_cache off; chunked_transfer_encoding off; }
常见错误
这一步最容易踩的坑
下面每一条都对应一个真实会发生的故障。先读完再动手,比出问题之后再回来查要省时间。
- 后端监听 0.0.0.0,公网可以绕过代理直连端口。这是反向代理场景里最常见的安全漏洞。
- 缺少 X-Forwarded-Proto,框架生成 http 链接,浏览器陷入重定向循环。排查半天才发现是一行 header 的事。
- WebSocket 握手返回 400 或 426,几乎总是缺 Upgrade/Connection 头或忘了 proxy_http_version 1.1。
- 上传报 413,是 client_max_body_size 没调;注意 Nginx 和后端框架两边都可能有各自的限制。
- set_real_ip_from 写成 0.0.0.0/0。任何人都能伪造 X-Forwarded-For,你的限速与封禁形同虚设。
- 502 时只盯着 Nginx 看。先确认后端进程还活着、端口没变 —— pm2 list 或 systemctl status 一眼就知道。
- SSE/流式接口忘了关 proxy_buffering,表现为消息卡住不动直到连接结束。
这篇教程的边界
- 命令以 Debian 12 为准。Ubuntu 与 Rocky Linux 的差异只在正文明确标注之处,未标注的部分请以你所用发行版的官方文档为准。
- 示例中的 IP 来自文档保留段 203.0.113.0/24,域名为 example.com,端口为示意值。复制后必须替换成自己的值, 原样执行不会有任何效果。
- 教程不能替代备份。任何会改动数据或线上流量的步骤,执行前先确认有一份验证过能恢复的备份。
- 本站不提供任何用于绕过网络审查的配置或说明,这篇也不例外。
发现命令过时或有误,请发邮件到 [email protected]。 指出错误的邮件比沉默的旧文档有价值得多。