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);

它会影响:

不要在不了解代理拓扑时无条件:

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 的 fileSizefiles 等限制。

首次普通 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 与反向代理的基本关系。

复盘题

  1. Nginx 是如何找到 PM2 管理的 Express 应用的?
  2. proxy_pass 有无尾部 / 对 URI 有什么影响?
  3. 为什么配置变更后先执行 nginx -t
  4. trust proxy 为什么必须结合代理拓扑和端口暴露情况?
  5. Nginx 返回 502 时应按什么顺序排查?

官方参考