反向代理基础
这一课你将能做到
- 正确写出两种
proxy_pass(末尾有/无 /),并推演 /api/tracks 到达 Spring Boot 后的路径
- 配齐
Host、X-Real-IP、X-Forwarded-For、X-Forwarded-Proto,说明 Spring / Next 为何需要它们
- 为经 Nginx 的 Next 开发态补上
Upgrade / Connection,避免 WebSocket/HMR 失败
为什么要学
反向代理是本 track 的核心技能:CodeRoadMap 所有公网 /api 与页面流量都经 proxy_pass 进入上游。
写错尾斜杠 → 404;丢了转发头 → 后端以为自己是 http、限流记错 IP、redirect 跳错域;缺 Upgrade → 本地经 Nginx 调试时热更新挂掉。
本课把「转发语义 + 必备头 + 超时/体大小」一次钉牢,下一课再组装完整双 upstream。
先建立图像
客户端请求
Host: www.coderoadmap.cn
URL: /api/tracks
│
▼
location /api/ + proxy_pass http://backend/;
│ 去掉 /api/ 前缀
▼
上游收到
URL: /tracks
额外头: X-Forwarded-Proto=https, X-Real-IP=客户端, …
| 写法 | 行为直觉 | 本站用法 |
|---|
proxy_pass http://upstream/;(有尾 /) | 常与 location 前缀替换相关 | /api/ → 剥前缀进 Spring |
proxy_pass http://upstream;(无尾 /) | 更常保留原 URI | / → 原样进 Next |
人话:proxy_pass 的尾斜杠像「是否撕掉信封上的部门代号再投递」。撕错了,收件人桌牌对不上。
跟着做
步骤 1:读官方 proxy_pass 说明
打开 ngx_http_proxy_module — proxy_pass,关注:当 proxy_pass 带 URI 时,location 匹配部分会被替换。
用中文记:「带路径的 proxy_pass 会改写 URI;只写 scheme+host(可含 upstream 名)时通常保留原请求 URI。」
步骤 2:对照生产两段
打开 deploy/nginx/prod.conf.example:
location /api/ {
# 尾 / :/api/tracks → 上游 /tracks(Spring 映射不含 /api 前缀)
proxy_pass http://coderoadmap_backend/;
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;
}
location / {
# 无尾 / :/tracks/foo → 上游 /tracks/foo
proxy_pass http://coderoadmap_platform;
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;
# Next 开发 HMR / WebSocket 需要
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
/api/auth/login → :8080/auth/login
/api/tracks → :8080/tracks
/tracks/java-fundamentals → :3000/tracks/java-fundamentals
步骤 3:本机 curl 对照(有 upstream 时)
curl -sS -o /dev/null -w "%{http_code}" http://127.0.0.1:8080/tracks
curl -sS -o /dev/null -w "%{http_code}" https://test.example/api/tracks
# 或 http 实验环境
应看到:两者业务结果一致(路径已正确剥离)。若直连 200、经 Nginx 404,优先查 尾斜杠与 location 前缀。
步骤 4:必备头 — 逐项人话
proxy_set_header Host $host; # 上游看见的是公网域名,不是 127.0.0.1
proxy_set_header X-Real-IP $remote_addr; # 单跳真实客户端 IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 链式追加
proxy_set_header X-Forwarded-Proto $scheme; # https,供后端生成正确绝对 URL / cookie Secure
Spring Boot 需信任转发头(server.forward-headers-strategy 等,见安全课),否则 redirect、限流、日志会偏。
步骤 5:WebSocket / HMR
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
本地 npm run dev 经 Nginx 时,缺这两项常见症状:热更新断开、WS 101 失败。生产静态构建的 Next 对 HMR 依赖低,但保留无害,且利于未来实时能力。
步骤 6:超时与上传体积
client_max_body_size 20m; # 与课程封面等上传对齐;过小 → 413
proxy_connect_timeout 60s;
proxy_send_timeout 60s;
proxy_read_timeout 60s; # 上游慢/长请求时避免过早 504
与 spring.servlet.multipart 上限一起看,两边都要够。
讲清楚要点
- 因为 Spring 的映射通常不含网关前缀
/api,所以 location /api/ 配 proxy_pass …/; 剥离前缀——前后端契约一致。
- 因为 Next 路由依赖完整路径,所以页面 location 用无尾
/ 的 proxy_pass——避免误改 URI。
- 因为上游默认只看见代理连接,所以必须补
X-Forwarded-* 与 Host——协议、域名、IP 才正确。
- 因为 HTTP/1.1 升级协议需要协商,所以 WebSocket 要传 Upgrade/Connection——否则长连接建不起来。
- 因为上传受 Nginx 与应用双重限制,所以
client_max_body_size 要与 Spring multipart 对齐——只改一边仍会失败。
- 因为 502/504 常来自上游挂了或超时,所以排错先看 error.log 与上游是否 listen——不要只改浏览器。
- 因为官方对 proxy_pass URI 替换规则严格,所以改路径时先推演再 reload——用 curl 验证比猜快。
你可能会懵的地方
-
为什么有人写 proxy_pass http://127.0.0.1:8080/; 不用 upstream 名?
可以。upstream 的好处是:多实例、keepalive、换 IP 只改一处。本站生产用命名 upstream。
-
X-Forwarded-For 能信任吗?
只信任你管控的上一跳写入的值。若前面还有 CDN,要用 real_ip 模块声明可信源,否则客户端可伪造(见安全头课)。
-
proxy_pass 末尾多一个空格或少斜杠?
语法可能仍过 -t,但运行路径全错。改完用同一条 curl 回归。
-
Connection 写成空字符串又是什么?
给 keepalive 复用上游连接时,常 proxy_set_header Connection "";(缓存/性能课)。与 WebSocket 的 "upgrade" 场景不同,不要混用错 location。
错法对照
| 错法 | 问题 | 正解 |
|---|
/api/ + proxy_pass http://backend;(无尾 /) | 上游收到 /api/tracks,Spring 404 | 使用 http://backend/; 剥离 |
/ + proxy_pass http://platform/;(多尾 /) | 路径被错误改写 | 页面用无 URI 的 proxy_pass |
不设 X-Forwarded-Proto | 后端以为 http,redirect/cookie 错 | 设置 $scheme |
无 Upgrade 头经 Nginx 跑 next dev | HMR/WS 失败 | 补 Upgrade + Connection |
client_max_body_size 默认 1m | 上传 413 | 提到与业务一致(如 20m) |
自测三问
/api/tracks 经本站生产写法后,Spring 收到的路径是什么?
- 列出至少三个应
proxy_set_header 的头及目的。
- WebSocket 场景下,
Connection 头通常设成什么?
小结一句话
proxy_pass 尾斜杠决定路径如何改写;配齐转发头与 Upgrade,后端才能像「直连公网」一样正确工作。
官方延伸