必须显式配置 proxy_set_header Host $http_host;,因为 Nginx 默认用 $proxy_host(即 upstream 地址)覆盖原始 Host,导致后端收到错误的 Host 头;$http_host 精确保留客户端请求中的域名和端口,避免重定向、CORS、Cookie 等问题。

要在 Nginx 中让 proxy_pass 转发时保留原始 Host 头,关键不是“默认保留”,而是**必须显式覆盖 Nginx 的默认行为**——否则它会把 Host 设为 upstream 地址(比如 127.0.0.1:3000 或 backend:8080),导致后端看到的 Host 完全失真。
为什么默认不保留 Host?
Nginx 在执行 proxy_pass 时,若未配置 proxy_set_header Host,就会自动使用 $proxy_host 变量(即 proxy_pass 后面的地址部分)作为 Host 请求头发送给后端。这个值是代理内部标识,和用户访问的域名毫无关系。
- 浏览器请求
https://admin.example.com:8443/api/login→ 原始 Host 是admin.example.com:8443 - 若 Nginx 没配
proxy_set_header Host→ 后端收到 Host =127.0.0.1:3000或backend:8080 - 后果:重定向跳错、静态资源 404、Cookie 域名失效、JWT 签名校验失败
正确写法:用 $http_host,不是 $host
推荐配置:proxy_set_header Host $http_host;
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
$http_host 直接取自客户端原始请求头,完整保留域名和端口(如 api.dev:8080 或 app.example.com:443)。而 $host 会自动剥离标准端口(80/443),在非标端口或需要精确校验的场景下不可靠。
- 客户端 Host 头是
test.local:8080→$http_host=test.local:8080,$host=test.local - 后端若用 Host 构造跳转 URL 或做 CORS 白名单,缺端口就直接出错
- HTTPS 请求中带
:443是合法且常见的,$http_host才能如实透传
多层代理必须每层都配
proxy_set_header 的作用域仅限当前 location 或 server 块,且只对紧随其后的 proxy_pass 生效,不会继承、不会穿透。
- 链路示例:CDN → ALB → Nginx → 应用
- 只要其中任意一层没配
proxy_set_header Host $http_host;,Host 就会在那中断 - 不能只在最后一层 Nginx 配,前面的负载均衡器或网关也必须同步配置
完整 location 示例
以下是最小可靠配置,适用于大多数反向代理场景:
location /api/ {
proxy_pass http://127.0.0.1:3000/;
proxy_set_header Host $http_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;
}
-
proxy_pass末尾加/表示剥离/api/前缀,转发到后端根路径 -
X-Forwarded-Proto用于传递协议(HTTP/HTTPS),避免后端误判安全上下文 - 所有
proxy_set_header行必须放在proxy_pass之后(顺序不影响生效,但习惯上放后面更清晰)

















