X-Forwarded-Host不能写死,因会覆盖多域名真实请求头,破坏租户识别和URL生成;必须用$host或$http_host动态透传,且需在location/server块中proxy_pass前单独配置,避免跨层级覆盖。

为什么 X-Forwarded-Host 不能直接写死
很多配置里直接写 proxy_set_header X-Forwarded-Host example.com;,结果后端收不到真实请求的 Host,或多个域名共用一个 upstream 时全部被覆盖成同一个值。这是因为 Nginx 默认不会自动透传客户端原始 Host,而写死值会破坏多站点路由逻辑——比如后端靠 X-Forwarded-Host 做租户识别或生成绝对 URL,一写死就全乱套。
- 必须用变量动态取值,最常用的是
$host(不含端口)或$http_host(含端口,且保留原始大小写和冒号分隔) -
$host在 Host 头缺失时会回退为 server_name,而$http_host是严格取请求头,更贴近客户端真实发送的内容 - 如果后端依赖完整 Host(如
api.example.com:8080),优先用$http_host;若只要域名部分(api.example.com),$host更稳妥
proxy_set_header X-Forwarded-Host 的典型位置和顺序问题
这个指令必须放在 location 块或 server 块里,且要放在 proxy_pass 之前;否则 Nginx 不会把它注入到转发请求中。更关键的是:它不能被重复定义——如果在 http 块里设了一次,又在 server 里覆盖一次,Nginx 会以**最内层生效的为准**,外层的会被完全忽略。
- 推荐只在
location或server级设置,避免跨层级覆盖混乱 - 不要和
proxy_redirect off;混用以为能“禁用重写”,它和X-Forwarded-Host无关 - 若用了
proxy_set_header Host $host;,注意它和X-Forwarded-Host是两个独立头,前者影响后端收到的Host,后者只是透传信息,别误以为设了其中一个另一个就自动同步
多站点共用 upstream 时如何确保 X-Forwarded-Host 正确
当多个 server 块(如 site-a.com、site-b.com)反向代理到同一个 upstream,但后端需要区分来源站点,仅靠 upstream 定义无法携带 Host 上下文。此时必须在每个 server 的 location 中显式设置 X-Forwarded-Host,而不是在 upstream 或全局 http 块里统一配。
- 错误写法:
upstream backend { server 127.0.0.1:8000; } http { proxy_set_header X-Forwarded-Host $host; }—— 这里的$host在http块中无意义,变量不可用 - 正确写法:每个
server块内单独配server { server_name site-a.com; location / { proxy_pass http://backend; proxy_set_header X-Forwarded-Host $http_host; } } - 如果后端还依赖协议(HTTP/HTTPS),建议一并传
X-Forwarded-Proto $scheme;,避免混合内容或跳转出错
验证是否生效:从后端日志和 curl 调试入手
光看 Nginx 配置没用,得确认后端实际收到了什么。常见误区是只检查 Nginx access_log,但它默认不记录请求头;或者用浏览器访问,却忽略了浏览器可能自动补全 Host 导致干扰。
- 临时在后端加一行日志,打印
X-Forwarded-Host头值(例如 Python Flask:request.headers.get("X-Forwarded-Host")) - 用
curl -H "Host: test.example.com" http://your-nginx-ip/手动指定 Host,再比对后端收到的值 - 注意 Nginx 默认不传递空头,如果客户端没发 Host 头(极少见),
$http_host为空字符串,后端需做空值容错 - 如果后端是 Node.js + Express,记得启用
app.set("trust proxy", true),否则req.hostname可能仍取自Host而非X-Forwarded-Host
Nginx 不会自动帮你做 Host 透传,$host 和 $http_host 表现差异很小但关键场景会出问题,而多站点共用 upstream 时最容易漏掉 per-server 的 header 设置——这地方一省事,后端就收不到真实 Host。











