关键是要确保每层代理都用proxy_set_header Host $http_host原样传递原始Host头,避免端口丢失或被内网地址覆盖,并配合X-Forwarded-*头增强可信度。

要让后端服务器准确识别客户端访问的是哪个域名,关键不是“转发 Host”,而是**确保每一跳代理都把最原始的 Host 值原样传下去**。多层代理中只要漏配一层,后端看到的就是内网地址(比如 127.0.0.1:3000 或 backend-svc),导致静态资源 404、重定向跳错、Cookie 域名不匹配等问题。
必须用 $http_host,不是 $host
客户端请求头里的 Host 是真实意图的体现,可能带端口(如 api.example.com:8443)。$host 会自动剥离 80/443 端口,而 $http_host 是原样复刻——后端若依赖端口做签名校验或生成跳转链接,用 $host 就会失败。
- ✅ 推荐写法:
proxy_set_header Host $http_host; - ❌ 避免写法:
proxy_set_header Host $host;(端口丢失风险) - ❌ 绝对禁用:
proxy_set_header Host $proxy_host;(这是 upstream 地址,和用户无关)
每层 Nginx 都要单独配置
Host 头不会自动透传,Nginx 默认用 $proxy_host 覆盖它。哪怕你只改最后一层,前面某层没设,Host 就已在那中断了。
- 第一层(面向公网,如 CDN 后的入口 Nginx):location 块里加
proxy_set_header Host $http_host; - 中间层(如负载均衡器或网关 Nginx):同样位置加同一行,不省略
- 最后一层(直连应用):仍需加,否则后端收到的是
10.0.1.5:8080这类内部地址
配合 X-Forwarded-* 提高可信度
单靠 Host 不够,后端常需交叉验证。加这两行能避免因 Host 被篡改或覆盖导致的误判:
-
proxy_set_header X-Forwarded-Proto $scheme;(告诉后端是 http 还是 https) -
proxy_set_header X-Forwarded-Host $http_host;(提供一份原始 Host 的备份,比直接信 Host 更安全)
检查干扰项,防止静默失效
配置写了不一定生效,常见“看似配了实则没用”的情况:
- 同一 location 内出现多次
proxy_set_header Host,后写的会覆盖前写的 - 启用了
underscores_in_headers on;,虽不直接禁 Host,但若其他自定义头含下划线,Nginx 可能整条请求头解析失败 - 后端框架(如 Spring Boot 某些版本)会主动重写 Host 为本地地址,需查日志确认是“没收到”还是“收到后被覆盖”
改完务必运行 nginx -t 验证语法,再 nginx -s reload 生效。


















