排查多级代理下ip_hash失效,核心是验证nginx哈希输入是否为用户原始ip:一要通过set_real_ip_from+real_ip_header还原$remote_addr为真实ip并日志验证;二要确保x-forwarded-for全链路透传;三要检查upstream中ip_hash顶格配置且后端列表稳定;四要后端信任x-real-ip而非伪造xff。

排查多级代理下 ip_hash 失效,核心是验证“Nginx 真实拿到的哈希输入是否为用户原始 IP”。不是看配置有没有写 ip_hash,而是看它算的时候用的是谁的地址。
一、确认 $remote_addr 是否已被还原为真实客户端 IP
这是最关键的一步。默认情况下,$remote_addr 是直连 Nginx 的上一跳地址(比如 F5、SLB 或 CDN 的内网 IP),不是用户真实 IP。
- 在 Nginx 的
http或server块顶部(location之前)检查是否配置了:set_real_ip_from—— 列出所有可信代理段(如10.0.0.0/8、172.16.0.0/12、192.168.0.0/16,或 Cloudflare/阿里云 SLB 的官方回源段)real_ip_header X-Forwarded-For(若用 Cloudflare 则为CF-Connecting-IP)real_ip_recursive on - 添加日志字段验证:
log_format debug '$remote_addr | $realip_remote_addr | $http_x_forwarded_for - $request';
查看 access 日志:如果$realip_remote_addr显示公网 IP(如203.208.60.123),而$remote_addr仍是内网地址(如10.0.1.5),说明还原成功;否则ip_hash仍在对内网 IP 哈希。
二、逐跳检查 X-Forwarded-For 是否完整透传
任何一级代理覆盖、清空或错误拼接 X-Forwarded-For,都会导致最末端 Nginx 无法提取真实 IP。
- 首层代理(直连客户端)必须用
$remote_addr初始化该头:proxy_set_header X-Forwarded-For $remote_addr; - 后续每一层(WAF → SLB → Nginx LB)必须用:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
(注意是_add_,不是$http_x_forwarded_for或硬写$remote_addr) - 用
curl -v从外网发起请求,逐跳抓包或查日志,确认X-Forwarded-For值是否持续增长(如203.208.60.123, 104.154.20.3, 10.0.1.5),且最左 IP 始终是你预期的真实客户端 IP。
三、验证 ip_hash 配置本身是否生效
即使 IP 正确了,配置细节错误也会让 ip_hash 形同虚设。
-
ip_hash必须顶格写在upstream块第一行,不能缩进,不能和hash、least_conn等混用 - 后端列表不能动态变更:增删
server行会重置哈希映射,大量用户 session 漂移 - IPv4 默认只取前三段哈希(
192.168.1.100和192.168.1.200视为相同),企业 NAT 出口下易集中打到单台——此时需还原真实内网 IP 或改用其他策略 - 不支持
weight、backup,加了会导致 Nginx 启动失败
四、检查后端服务是否误读 IP
Nginx 还原了 $remote_addr,但后端代码若仍读 REMOTE_ADDR 或直接解析 X-Forwarded-For 字符串,就等于前功尽弃。
- 后端应优先读取
X-Real-IP头(由 Nginxrealip_module校验后注入,可信) - 禁用直接信任
X-Forwarded-For的逻辑,尤其不能无条件取第一个值——该头可被客户端伪造 - Spring Boot 可配
server.forward-headers-strategy=framework并启用RemoteIpFilter;PHP 应设置trust_proxy相关参数











