多级反向代理中请求头被重复覆盖的本质是某层主动重写关键头(如host、x-forwarded-for)且后续层未正确继承,排查需从最外层逐跳验证请求头原始状态,重点检查proxy_set_header继承断点、host透传是否显式声明为$http_host、x-forwarded-for是否使用$proxy_add_x_forwarded_for追加而非覆盖,并确认后端代码是否正确解析可信ip。

多级反向代理中请求头被重复覆盖,本质是某一层主动重写了关键头(如 Host、X-Forwarded-For、X-Real-IP),而后续层又未正确继承或修正,导致原始值丢失、策略失效或后端行为异常。排查重点不在“有没有传”,而在“谁改了、怎么改的、是否覆盖而非追加”。
确认哪一层最先破坏了请求头
从最外层入口开始逐跳验证,用 curl -v 模拟真实请求,观察每层响应前收到的请求头原始状态:
- 直接访问第一层(如 CDN 或公网 Nginx):检查 X-Forwarded-For 是否含你的真实公网 IP,Host 是否为你访问的域名;
- 若正常,再请求第二层(如内部网关 Nginx):对比请求头是否被篡改,尤其注意 Host 是否变成 upstream 名称(如
origin_upstream)或内网地址; - 常见触发点:某层 location 块中只加了
proxy_set_header Connection $http_connection;这类非 Host 头配置,却意外中断了 server 级别的 Host 继承,默认回退为$proxy_host。
检查 proxy_set_header 的继承断点
Nginx 默认仅在当前配置块未定义任何 proxy_set_header 时才继承上层设置;一旦定义任意一条,就会丢弃所有默认继承(包括 Host 和 Connection),并启用内置默认值。
- 查看各层 server 和 location 块:是否存在只写了一两条
proxy_set_header却漏掉Host的情况; - 特别警惕硬编码写法:
proxy_set_header Host example.com;或proxy_set_header Host $proxy_host;——它们会强制覆盖原始 Host; - 正确做法:在每一级 location 中显式声明
proxy_set_header Host $http_host;,确保透传原始值。
验证 X-Forwarded-For 是否被覆盖而非追加
中间层若用 $remote_addr 或 $http_x_forwarded_for 而非 $proxy_add_x_forwarded_for 设置头,会导致链路断裂。
- 在每一层 Nginx 的 log_format 中加入
$http_x_forwarded_for,记录进 access_log; - 比对相邻两层日志:前层日志中的 X-Forwarded-For 值,是否完整出现在后层日志的同一字段开头;
- 若后层只看到一个 IP(如 10.0.1.5),说明该层覆盖了头;若看到两个 IP 但顺序颠倒或缺失首段,说明用了
$http_x_forwarded_for而非$proxy_add_x_forwarded_for。
定位后端读取逻辑是否依赖被污染的头
即使头传到了,如果业务代码仍读 REMOTE_ADDR 或错误解析 X-Forwarded-For,等于白传。
- 在最终应用层打印所有相关头:
Host、X-Forwarded-For、X-Real-IP、X-Forwarded-Proto; - 确认代码是否信任了
X-Forwarded-For的第一个 IP,还是直接取了最后一个(易被伪造); - Nginx 中建议用 map 提取可信客户端 IP:
map $http_x_forwarded_for $real_client_ip {
~^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$ $1;
default $remote_addr;
}
再在 allow/deny 或限流中使用$real_client_ip。











