05 Nginx 反向代理 Express
本章假设你已经会使用 Nginx 返回前端静态页面,只增加 /api/ 反向代理。
1. 最小可运行配置
假设:
前端目录:/var/www/frontend
Express:127.0.0.1:8080
域名:example.com
可以从下面的 server block 开始:
server {
listen 80;
server_name example.com;
root /var/www/frontend;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
location /api/ {
proxy_pass http://127.0.0.1:8080;
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;
}
}
这里保留了请求中的 /api/ 前缀:
浏览器:GET /api/users
Express:GET /api/users
因此 Express Router 也应按 /api 组织。
2. Nginx 与 PM2 如何连接
Nginx 不直接调用 PM2:
Nginx
→ TCP 连接 127.0.0.1:8080
→ 当前监听该端口的 Express 进程
PM2 的职责是保证 Express 进程运行。Nginx 只关心 127.0.0.1:8080 是否有可用上游。
因此:
PM2 应用停止
→ 8080 无人监听
→ Nginx 无法连接上游
→ 通常返回 502 Bad Gateway
3. proxy_pass 尾部斜杠
这是最容易踩的坑之一。
不带 URI 尾部斜杠
location /api/ {
proxy_pass http://127.0.0.1:8080;
}
通常保留原始 URI:
/api/users → /api/users
带 URI 尾部斜杠
location /api/ {
proxy_pass http://127.0.0.1:8080/;
}
匹配到的 /api/ 前缀会被 / 替换:
/api/users → /users
两种配置都可以,必须与 Express 路由约定一致。不要通过反复试斜杠碰运气,应先明确后端希望收到哪个路径。
4. 转发 Header 的作用
proxy_set_header Host $host;
让上游知道用户访问的域名。
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
传递客户端 IP 代理链。
proxy_set_header X-Forwarded-Proto $scheme;
告诉 Express 外部访问使用 HTTP 还是 HTTPS。
这些 Header 由代理写入,但 Express 是否信任它们由 trust proxy 决定。
5. Express 的 trust proxy
当 Express 前面固定只有一层可信 Nginx,并且 8080 不对公网开放,可以配置:
app.set("trust proxy", 1);
它会影响:
req.ip;req.protocol;req.secure;- Secure Cookie;
- express-rate-limit 获取客户端 IP;
- 应用访问日志。
不要在不了解代理拓扑时无条件:
app.set("trust proxy", true);
如果客户端能够绕过 Nginx 直接访问 Express,可能伪造转发 Header。防火墙、云安全组和监听地址应共同保证代理边界。
6. 检查配置后再 reload
编辑完成后:
sudo nginx -t
只有成功时才执行:
sudo systemctl reload nginx
Nginx 官方说明,reload 时 master 会检查新配置;成功后启动新 worker,并让旧 worker 完成已有请求后退出。如果新配置无法应用,会继续使用旧配置。
常用 systemd 命令:
sudo systemctl status nginx
sudo systemctl start nginx
sudo systemctl stop nginx
sudo systemctl restart nginx
sudo systemctl reload nginx
日常修改配置优先使用 reload。
7. 分层测试,不要直接从浏览器猜
第一步:Express 是否工作
curl -i http://127.0.0.1:8080/health
第二步:Nginx 本机转发是否工作
curl -i -H "Host: example.com" http://127.0.0.1/api/health
第三步:公网域名
curl -i http://example.com/api/health
如果第一步失败,先看 PM2/应用;第一步成功但第二步失败,再看 Nginx。
8. 上传和请求大小
Nginx 可能在请求到达 Express/Multer 前就拒绝大请求。上传接口需要明确设置整体请求体上限:
location /api/uploads/ {
client_max_body_size 12m;
proxy_pass http://127.0.0.1:8080;
}
这限制的是整个 HTTP 请求体,不只是文件。还应同时使用 Multer 的 fileSize、files 等限制。
首次普通 JSON API 可以保留发行版默认值,遇到明确上传需求时再单独配置,不要一上来把全站上限改到数 GB。
9. timeout 暂时怎么处理
第一版先使用默认值,只有确认某个合法接口需要更长时间时再调整。例如:
location /api/reports/ {
proxy_pass http://127.0.0.1:8080;
proxy_read_timeout 60s;
}
proxy_read_timeout 是两次从上游读取数据之间允许的时间,不是所有情况下都等于请求绝对总时间。它也不会自动停止 Express 已经开始的后台任务。
不要用无限延长 Nginx timeout 掩盖慢 SQL;长报表更适合后台任务。
10. HTTPS 放在下一步
首次可以先在受控环境验证 HTTP 链路,但正式公网业务尤其是登录、Cookie、Token 和个人信息接口必须使用 HTTPS。
HTTPS 通常由 Nginx 终止:
浏览器 HTTPS
→ Nginx 解密
→ 本机 HTTP 127.0.0.1:8080
证书申请和续期可以单独学习,不影响理解 PM2 与反向代理的基本关系。
复盘题
- Nginx 是如何找到 PM2 管理的 Express 应用的?
proxy_pass有无尾部/对 URI 有什么影响?- 为什么配置变更后先执行
nginx -t? trust proxy为什么必须结合代理拓扑和端口暴露情况?- Nginx 返回 502 时应按什么顺序排查?