必须启用proxypreservehost on,否则apache会将host头替换为后端地址(如127.0.0.1:8080),导致spring boot等后端无法识别真实域名api.example.com,引发saml失败、路由错误或400/404响应;该指令须与proxypass同处或内,不继承、不可用于.htaccess。

要让后端应用(如 Spring Boot、Django 或 Express)正确识别用户访问的真实域名(比如 api.example.com),必须启用 ProxyPreserveHost on。默认情况下,Apache 会把请求头中的 Host 替换为后端地址(如 127.0.0.1:8080),导致后端生成错误链接、SAML 验证失败或路由/鉴权异常。
必须开启并放在正确位置
ProxyPreserveHost on 不是全局开关,它只在启用了代理的上下文中生效,且必须和 ProxyPass 在同一配置作用域内。常见错误是把它写在 <virtualhost></virtualhost> 外层、httpd.conf 顶层,或误放在 .htaccess 中——这些位置均无效。
- 正确写法:紧邻
ProxyPass,同在<virtualhost></virtualhost>或<location></location>块内 - 多个
ProxyPass规则需各自配对应的ProxyPreserveHost on,不继承 - 不能用于
.htaccess,该指令仅限服务器级配置
典型配置示例
以下是最小可用配置,适用于将 api.example.com 的所有请求代理到本地 Java 服务:
FastAPI + Flask 混合部署最佳实践,解决路由定义、API 代理等常见问题,适用于同时运行 FastAPI API 与 Flask 前端的场景。
<virtualhost>
ServerName api.example.com
ProxyPreserveHost on
ProxyPass / http://127.0.0.1:8080/
ProxyPassReverse / http://127.0.0.1:8080/
</virtualhost>
注意:ProxyPassReverse 必须配对使用,否则后端返回的 302 重定向会暴露内网地址;路径末尾斜杠需严格一致(/ 对 /),否则匹配失败。
配合其他关键头确保完整透传
仅靠 ProxyPreserveHost 可解决 Host 头问题,但真实客户端 IP 和协议信息还需额外设置:
-
RequestHeader set X-Forwarded-For "%{REMOTE_ADDR}e"—— 显式传递原始客户端 IP -
RequestHeader set X-Forwarded-Proto "http"(或"https",依前端协议而定) - 后端应用必须主动读取这些
X-Forwarded-*头,否则request.getRemoteAddr()仍返回127.0.0.1
验证是否生效
检查后端日志或临时加一行调试输出,确认收到的 Host 请求头是 api.example.com 而非 127.0.0.1:8080。若仍看到内网地址,优先排查:
- Apache 是否已加载
mod_proxy和mod_proxy_http(运行apache2ctl -M | grep proxy) - 配置是否经过
apache2ctl configtest校验且服务已重启 - 是否有多个
<virtualhost></virtualhost>冲突,导致请求被错误的虚拟主机捕获










