最稳妥做法是用$host,因其优先取客户端Host头、缺失时fallback到server_name、自动小写并剥离80/443端口,适配路由、跳转、CORS等常见场景;禁用$http_host(端口易致签名失败)和$proxy_host(暴露内网地址)。

直接在 location 或 server 块中写 proxy_set_header Host $host; 就能保持原始请求的域名,这是最常用也最稳妥的做法。
为什么用 $host 而不是其他变量
$host 会优先取客户端请求里的 Host 头,如果没提供就 fallback 到 server_name,还会自动转为小写、去掉 :80 或 :443 端口——这对大多数后端应用更友好,比如路由匹配、生成跳转链接、CORS 判断等场景都依赖干净的域名。
- 不用
$http_host:它原样保留端口(如example.com:8443),容易导致签名失败或重定向缺协议 - 禁用
$proxy_host:它等于proxy_pass后面的地址(如127.0.0.1:3000),完全丢失原始上下文 - 不写这行 = 默认用
$proxy_host,等于主动丢掉原始域名
多层代理时怎么确保不被覆盖
每经过一层 Nginx,都要显式配置一次 proxy_set_header Host $host;。否则中间某层没设,就会把 Host 改成它自己的 upstream 地址,后端最终收到的就是内网名或 IP。
- CDN → 边缘 Nginx → 应用网关 → 后端服务:四层都得加这一行
- 避免被覆盖:确认该 location 中没有其他
proxy_set_header Host指令重复出现 - 调试技巧:在后端打印
request.headers.get('Host'),或用curl -v http://your-domain/观察实际发给后端的值
需要保留端口时怎么办
极少数后端严格校验完整 Host 字符串(含端口),比如某些 API 签名逻辑。这时改用 $http_host:
proxy_set_header Host $http_host;- 注意:
$http_host在客户端没带Host头时为空,Nginx 会返回 400 错误(符合 HTTP/1.1 规范) - 若前端是 CDN,且它已把原始
Host写进X-Forwarded-Host,可改用$http_x_forwarded_host更可靠
配套建议:让后端真正用上这个 Host
只传 Host 头还不够,后端得知道该信谁:
- Spring Boot:配
server.forward-headers-strategy=framework,并信任你的 Nginx IP - Django:启用
USE_X_FORWARDED_HOST = True,并确保ALLOWED_HOSTS包含$host对应的域名 - Node.js(Express):设
app.set('trust proxy', '127.0.0.1'),再用req.hostname - 日志里加
$host:在log_format中加入,方便比对各层是否一致











