核心是确认信任链是否被破坏:最外层代理必须用$remote_addr重置XFF,中间层仅追加不采信,Nginx需启用realip模块(set_real_ip_from+real_ip_recursive on)还原真实IP,后端须弃用XFF和REMOTE_ADDR,统一读X-Real-IP。

排查 X-Forwarded-For 伪造导致的 IP 控制失效,核心不是“怎么读头”,而是确认整个信任链是否被破坏:最外层代理是否重置了 XFF、中间层是否只追加不采信、Nginx 是否启用 realip 模块还原真实 IP、后端是否弃用不可信字段。
检查最外层代理是否强制重置 X-Forwarded-For
面向公网的第一跳(如 CDN、WAF 或边缘 Nginx)必须丢弃客户端传来的任何 X-Forwarded-For,仅用 $remote_addr 覆盖:
- 配置中应为 proxy_set_header X-Forwarded-For $remote_addr;,而非
$proxy_add_x_forwarded_for或$http_x_forwarded_for - 同时设置 proxy_set_header X-Real-IP $remote_addr;,为后端提供不可伪造的源 IP 字段
- 用
curl -v http://your-domain查看请求头,确认响应中 X-Forwarded-For 的第一个值是你预期的真实公网 IP,且无多余逗号分隔项
验证 realip 模块是否启用并正确配置
Nginx 默认不会用 X-Forwarded-For 替换 $remote_addr;必须显式启用 realip 模块,并告诉它“哪些上游是可信的”:
- 在
http{}块中添加可信代理段:set_real_ip_from 10.10.0.0/16;(内网 SLB)、set_real_ip_from 173.245.48.0/20;(Cloudflare 官方段,以最新为准) - 指定解析头:real_ip_header X-Forwarded-For;
- 多层代理时必须开启递归解析:real_ip_recursive on;,否则可能取到中间代理 IP 而非原始客户端 IP
- 配置生效后,
$remote_addr就会自动变为真实客户端 IP,$realip_remote_addr是等效别名,可用于日志或限流
确认后端服务是否绕过伪造风险读取 IP
即使 Nginx 配置正确,后端若仍按宽松逻辑 fallback,就等于把门敞开:
- 禁止写“先读 XFF,再 fallback 到 REMOTE_ADDR”的代码——这会让任意伪造的
X-Forwarded-For: 1.1.1.1生效 - Java 应统一使用
request.getHeader("X-Real-IP"),不调用getRemoteAddr() - PHP 应优先取
$_SERVER['HTTP_X_REAL_IP'],而非$_SERVER['HTTP_X_FORWARDED_FOR'] - Spring Boot 等框架需关闭 Tomcat 对 XFF 的自动清理(如配置
server.tomcat.remote-ip-header=为空),并确保 trust-proxy 已设为 true
实测策略是否真正作用于真实 IP
配置改完不等于生效,必须用真实请求验证控制逻辑是否落在正确 IP 上:
- 对 allow/deny 规则,用不同来源 IP(如手机热点、代理工具)发起请求,观察是否按预期放行/拒绝
- 对 limit_req,临时加一行日志:
log_format debug '$realip_remote_addr — $status — $request';,确认限流日志中的 IP 是你期望的真实地址,而非 10.x.x.x 或 127.0.0.1 - 若发现某层 Nginx 日志中
$http_x_forwarded_for出现空值或单个内网 IP,说明上一层未透传或覆盖失败,需回溯检查











