必须先还原真实客户端ip,再配置ip_hash:启用real_ip_module指定可信代理段、x-forwarded-for头及递归解析,确保$remote_addr为真实ip;各级代理透传xff;upstream中ip_hash须顶格且无动态变更;后端代码需信任代理ip。

解决 ip_hash 在多层反代级联时失效,核心不是“让 ip_hash 更强”,而是先确保它能拿到真实源 IP——否则哈希对象本身就是错的,再配十遍也白搭。
必须还原真实客户端 IP,否则 ip_hash 无从谈起
多层代理(比如:用户 → CDN → WAF → Nginx LB → 应用)下,$remote_addr 默认是上一跳代理出口 IP(如 10.0.1.5),所有请求哈希结果都一样,全打到同一台后端。必须启用 real_ip_module 剥离 X-Forwarded-For 中的真实 IP:
-
在 http 或 server 块顶部(location 之前)添加可信代理段:
set_real_ip_from 192.168.0.0/16;set_real_ip_from 10.0.0.0/8;set_real_ip_from 172.16.0.0/12;
(按你实际使用的 CDN/WAF/Nginx LB 回源 IP 段精确填写,不能写 0.0.0.0/0) -
指定解析来源头:
real_ip_header X-Forwarded-For;(若用 Cloudflare 则改用CF-Connecting-IP) -
开启递归解析:
real_ip_recursive on;——自动跳过不可信中间 IP,取最左侧可信来源
配置生效后,$remote_addr 就是真实用户 IP,ip_hash 才有正确输入。
反向代理链路上每层都要透传 X-Forwarded-For
仅 Nginx LB 这一层还原不够,上游每一层都得把真实 IP 逐级带下来:
- CDN/WAF 控制台必须开启「透传客户端真实 IP」功能(腾讯云叫“获取真实 IP”,Cloudflare 叫“True Client IP”)
- 每层 Nginx 的
location块中加:proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
($proxy_add_x_forwarded_for会自动追加当前$remote_addr,不覆盖已有值) - 避免手动拼接或覆盖 XFF,例如不要写
proxy_set_header X-Forwarded-For $remote_addr;——这会丢掉整条路径
ip_hash 本身配置要避开几个硬伤
即使 IP 正确了,配置不当仍会导致“看起来配了,其实没用”:
-
ip_hash 必须写在 upstream 块第一行,顶格,不能缩进,不能和
hash、least_conn等共存 -
upstream 后端列表不能动态变更:增删节点、临时
down一台,哈希映射整体偏移,大量用户 session 漂移 - IPv4 仅取前三段哈希:192.168.1.100 和 192.168.1.200 被视为同一 IP,NAT 网关后易集中打到单台
- 不支持 weight、backup:加了就报错,启动失败
后端代码也要配合信任代理
Nginx 还原了 $remote_addr,但后端框架若不认这个值,仍会读错:
- Spring Boot:
server.forward-headers-strategy=framework+ 显式配置server.tomcat.internal-proxies匹配你的可信网段 - PHP-FPM:
fastcgi_param REMOTE_ADDR $remote_addr;(别漏掉这行!宝塔常见坑) - Node.js Express:
app.set('trust proxy', '10.0.0.0/8, 192.168.0.0/16'); - 绝对不要依赖
request.getRemoteAddr()或$_SERVER['REMOTE_ADDR']原始值










