ip_hash失效根源在于$remote_addr非真实客户端ip,需通过realip模块修正并严格配置可信代理段、xff透传规则及禁用不兼容参数,再结合三步验证确保会话一致性。

ip_hash 失效的根源往往不在指令本身,而在于 Nginx 收到的 $remote_addr 已不是真实客户端 IP。多级代理(如 CDN → WAF → LB → Nginx 入口)会层层覆盖源 IP,导致所有请求被哈希成同一个值,负载彻底失衡。要修复,必须让 ip_hash 作用在“可信的真实 IP”上,而非代理出口 IP。
确保 ip_hash 作用于修正后的客户端 IP
ip_hash 默认对 $remote_addr 哈希,而该变量在多级代理下默认是上一跳代理的内网地址(如 10.0.0.5)。必须先用 realip 模块将 $remote_addr 替换为原始客户端 IP,再启用 ip_hash —— 否则哈希对象本身就是错的。
- 在 upstream 所属的 http 或 server 块中启用 realip,并严格限定可信代理段:
set_real_ip_from 192.168.100.0/24; # 内网 LB 网段
set_real_ip_from 203.208.60.0/24; # Google CDN 回源段
set_real_ip_from 173.245.48.0/20; # Cloudflare IP 段
real_ip_header X-Forwarded-For;
real_ip_recursive on; - 确认启用后,Nginx 日志中的 $remote_addr 已变为用户真实 IP(如 203.0.113.45),再配置 upstream:
upstream backend {
ip_hash; # 此时哈希的是 realip 修正后的 IP
server 192.168.1.10:8080;
server 192.168.1.11:8080;
}
全链路透传 X-Forwarded-For,避免中间层污染
realip 能正确提取的前提,是 X-Forwarded-For(XFF)头完整、可信、未被篡改。任何一层代理清空、覆盖或错误追加 XFF,都会导致最终解析失败。
- 首层代理(直连 CDN/WAF)必须用 $remote_addr 初始化:
proxy_set_header X-Forwarded-For $remote_addr; - 后续每一层(如中间 Nginx 网关)只追加、不覆盖:
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - 禁止在任意 location 中写 proxy_set_header X-Forwarded-For "" 或硬赋值为固定值
规避后端干扰与动态变更风险
即使 IP 识别准确,后端行为或 upstream 变动仍会导致会话漂移,表现为登录态丢失、购物车清空等现象。
- upstream 中禁用 weight、backup、max_fails 等参数 —— ip_hash 不兼容这些动态策略,新版本 Nginx 会直接报错
- 扩容或缩容 upstream server 时,哈希映射关系重算,用户会话必然中断;如需弹性伸缩,应改用 hash $remote_addr consistent;(stream 模块)或接入外部 session 存储
- 检查后端是否主动读取 X-Forwarded-For 并覆盖 session 绑定逻辑(例如 Spring Boot 中手动调用 getHeader("X-Forwarded-For")),应统一依赖 Nginx 修正后的 $remote_addr
验证与快速定位方法
不要依赖日志猜测,用三步交叉验证:
- 在 Nginx access_log 中加入 $remote_addr $http_x_forwarded_for,确认二者是否一致且为预期公网 IP
- 在后端应用中打印 request.getRemoteAddr() 和 HTTP_X_FORWARDED_FOR,比对是否与 Nginx 日志一致
- 用 curl 模拟多级链路:
curl -H "X-Forwarded-For: 203.0.113.45, 192.168.1.5" http://your-domain.com/api/test
观察该 IP 是否稳定命中同一台后端 server(可配合后端返回 server IP 辅助判断)










