速读#
反向代理不是为了好看,而是把对外入口统一起来,让浏览器别直接和后端进程对话。这篇最该记住三点:转发头要传对,proxy_pass 末尾斜杠别乱玩,改完先 nginx -t 再 reload。
为什么要反代#
| 收益 | 含义 |
|---|---|
| 统一入口 | 用户记域名,不记端口 |
| 隐藏后端 | 后端可绑回环,不直接暴露 |
| 集中加 HTTPS / 负载 | 放在入口层,后端不用各管各的 |
反向代理不是为了好看,是把「对外的一致性」和「对内的多样性」分开——对外一个域名 + HTTPS,对内一堆不同端口的服务。
正反代理:替谁出门#
- 正向代理:替客户端出门(翻墙)
- 反向代理:替服务器接客
用户只跟 Nginx 打交道,Nginx 再转给后面服务。用「替谁出门」记就不混。
一份反代配置,和栽过的转发头#
server { listen 80; server_name app.example.com; location / { proxy_pass http://127.0.0.1:8000; 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; }}不传这些头,后端只看到 Nginx 本地 IP,拿不到访客真实 IP 和协议。原因在反代的本质:后端收到的连接是 Nginx 发起的,不是用户发起的。 用户是谁、走没走 HTTPS,这些信息断在了 Nginx 这一跳,只能靠头显式传下去。
X-Forwarded-Proto 我栽过——漏了它,上 Cloudflare HTTPS 后 WordPress 无限重定向:用户明明走着 HTTPS,后端看到的却是 HTTP,于是执着地往 HTTPS 跳,跳完还是这样,循环。这行不是模板自带的装饰,是踩坑才认识价值。
末尾斜杠:不熟别玩花活#
proxy_pass 末尾带不带斜杠,行为不同。以 location /api/ 收到请求 /api/users 为例:
| proxy_pass 写法 | 后端收到 |
|---|---|
http://127.0.0.1:8000(不带) | /api/users |
http://127.0.0.1:8000/(带) | /users |
一个斜杠,决定 location 前缀剥不剥离。被人问过这个,我当时答不上来。
后来的判断:简单反代不带斜杠、location 用 /;需要路径改写时再查文档现核对。 不熟的时候别玩花活——这种一个字符改变语义的地方,靠印象写等于埋雷。
先验后发:-t && reload#
nginx -t && systemctl reload nginx| 步骤 | 作用 |
|---|---|
nginx -t | 语法检查;报错时旧配置仍生效 |
reload | 平滑重载,不断已有连接 |
改完别急着 reload。用 && 连成一条命令:只有测试通过才 reload,手不会比脑子快。
和 SSH 改配置「留当前会话」、防火墙启用前「先放行 SSH」同源:可能搞挂自己的操作前,先留活路 / 先验证。 Nginx 在这条纪律上还更友好——-t 失败时旧配置照常跑着,你有充足时间改。
后面加服务就是改端口#
- 对内:
proxy_pass指向绑回环(127.0.0.1)的容器或进程,公网摸不到后端 - 对外:HTTPS 统一加在这一层,后端全走明文回环
- 协议头:前面还有 CDN / 隧道时,
X-Forwarded-Proto要如实往后传,别让协议信息在中间断掉。真实 IP 同理——每多一跳,$remote_addr就只是上一跳的地址,访客是谁全靠转发头逐跳接力
Nginx 是接客的门卫,它的质量决定后面所有服务对外的体验。 不管后面是 Spring Boot、Django 还是 Node,思路一样——改的只是 proxy_pass 后面那个端口。
