Nginx的ip_hash基于$remote_addr,需通过realip模块将其设为客户端真实IP:启用--with-http_realip_module,配置set_real_ip_from限定可信代理段,real_ip_header指定X-Forwarded-For,开启real_ip_recursive on实现多层代理下安全取真实IP。

Nginx 的 ip_hash 负载均衡算法默认使用 $remote_addr 变量,也就是与 Nginx 直接建立 TCP 连接的上一跳 IP(通常是最后一层代理的 IP),而不是客户端真实 IP。当请求经过多层反向代理(如 CDN → LB01 → LB02 → Web)时,若不做特殊处理,ip_hash 会基于最末级代理(比如 LB02)的 IP 计算哈希,导致同一用户在不同请求中被分到不同后端,失去会话一致性。
要让 ip_hash 基于客户端真实 IP,关键不是“改算法”,而是让 Nginx 在执行 ip_hash 前,把真实 IP 正确赋值给它能识别的变量。以下是核心路径和实操要点:
✅ 必须启用 realip 模块并配置可信来源
ip_hash 本身不读取 X-Forwarded-For 或 X-Real-IP,它只认 $remote_addr。所以必须用 ngx_http_realip_module 把真实 IP “注入”到 $remote_addr 中:
- 编译时需包含
--with-http_realip_module(大多数发行版预编译包已启用,可用nginx -V 2>&1 | grep realip验证) - 在
http或server块中明确声明可信代理段(即你控制的各级 LB 的内网 IP 段):
set_real_ip_from 10.0.0.0/8; # 例如所有 LB 都在 10 网段 set_real_ip_from 172.16.0.0/12; set_real_ip_from 192.168.0.0/16; # 如果有具体 LB IP,也可单写:set_real_ip_from 10.0.0.5; real_ip_header X-Forwarded-For; # 指定从哪个 header 读取 IP 链 # real_ip_recursive on; # 可选:开启递归解析(推荐,见下文)
⚠️ 注意:
set_real_ip_from必须严格限定为你自己部署的代理节点 IP 段。不可写0.0.0.0/0,否则攻击者可伪造X-Forwarded-For绕过。
✅ 多层代理场景下正确传递与解析 X-Forwarded-For
每层代理都应追加自身 IP 到 X-Forwarded-For,格式为:X-Forwarded-For: 客户端IP, 代理1IP, 代理2IP
Nginx 的 realip 模块默认只取 X-Forwarded-For 最左边(第一个)IP,但这是不安全的——因为首字段易被伪造。
开启 real_ip_recursive on 后,Nginx 会从右往左扫描,跳过所有 set_real_ip_from 中定义的可信 IP,把第一个不在可信列表中的 IP 当作真实客户端 IP,并覆盖 $remote_addr。
例如:
- 请求头:
X-Forwarded-For: 203.0.113.42, 10.0.0.5, 10.0.0.3 -
set_real_ip_from 10.0.0.0/8→ 10.0.0.5 和 10.0.0.3 都被信任 -
real_ip_recursive on→ 从右往左,先跳过 10.0.0.3,再跳过 10.0.0.5,最终取203.0.113.42作为$remote_addr
这样 upstream { ip_hash; } 才真正基于用户真实 IP 哈希。
✅ 各层代理必须协同配置(不能只配最后一层)
最外层代理(如 CDN 或接入 LB):必须设置
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
($proxy_add_x_forwarded_for= 原 header + 自身$remote_addr,保证链式追加)中间层代理(如 LB01、LB02):同样要透传
X-Forwarded-For,且自身也应加入set_real_ip_from(如果它下游还有代理)最后一层 Nginx(运行 ip_hash 的那台):承担 realip 解析职责,必须配全
set_real_ip_from+real_ip_header+real_ip_recursive on
✅ 验证是否生效
- 查看 access log 中
$remote_addr是否已变成客户端真实 IP(而非 LB IP) - 在 upstream 块中临时加日志:
log_format debug '$remote_addr - $http_x_forwarded_for'; access_log /var/log/nginx/debug.log debug;
- curl 测试:
curl -H "X-Forwarded-For: 1.2.3.4, 10.0.0.5" http://your-domain/
观察日志中
$remote_addr是否为1.2.3.4
不复杂但容易忽略的是:ip_hash 的可靠性完全依赖 realip 模块能否准确还原 $remote_addr,而后者成败取决于你是否严格定义了可信代理段,并开启了递归解析。


















