nginx ip_hash在多级代理下失效的根本原因是$remote_addr取的是最近代理ip而非真实用户ip;需通过set_real_ip_from声明可信代理网段,并配合real_ip_header指定可信ip头(如x-forwarded-for或x-real-ip),使$remote_addr更新为真实ip,ip_hash方可正常工作。

在 Nginx 使用 ip_hash 负载均衡策略时,若上游服务部署在多级代理(如 CDN → LB → Nginx → 应用)后,ip_hash 常常“哈希错对象”——实际对最外层代理 IP(如 CDN 节点 IP)做哈希,导致用户会话漂移、负载不均。根本原因是 Nginx 默认用 $remote_addr(即直接 TCP 连接的客户端 IP)计算 hash,而该值在多级代理下已丢失真实用户 IP。
确认真实客户端 IP 是否被正确传递
先检查请求头中是否携带可信的原始 IP 字段(如 X-Forwarded-For、X-Real-IP),再确认这些字段是否来自可信代理。Nginx 不会自动信任所有 X-Forwarded-For,需配合 set_real_ip_from 显式声明可信代理网段:
- 在
http或stream块中配置可信代理地址(支持 CIDR):set_real_ip_from 10.0.0.0/8;set_real_ip_from 192.168.0.0/16;set_real_ip_from 203.208.60.0/22; # 示例:某 CDN 网段 - 指定用于覆盖
$remote_addr的请求头:real_ip_header X-Forwarded-For;(或X-Real-IP) - 启用后,
$remote_addr将自动变为解析出的真实客户端 IP,ip_hash即可基于它工作
验证 ip_hash 实际使用的 IP 来源
临时加一条日志,确认 ip_hash 内部到底在哈希哪个值:
- 在 upstream 块外定义 log_format:
log_format debug_hash '$remote_addr - $http_x_forwarded_for - $upstream_addr'; - 在 server 或 location 中启用该格式:
access_log /var/log/nginx/debug.log debug_hash; - 发起请求后查看日志,比对
$remote_addr是否已为真实用户 IP;若仍是中间代理 IP,说明real_ip配置未生效或代理链未正确透传
确保代理链端到端透传且顺序可信
多级代理中,X-Forwarded-For 是逗号分隔的 IP 链(如 a.b.c.d, x.y.z.w, 10.1.1.1),Nginx 默认取最左边(第一个)IP。但若前端有不可信代理,可能伪造该字段。因此:
-
必须严格按代理层级从外到内配置
set_real_ip_from:只添加你完全可控的上一级代理(如公司自建 LB),不添加 CDN 或第三方 LB(除非你确认其X-Forwarded-For可靠且仅追加) - 若 CDN 提供专用头(如
X-CDN-IP),优先使用它并配置:real_ip_header X-CDN-IP;real_ip_recursive off; - 禁用递归解析(
real_ip_recursive off)可避免取到中间代理 IP;开启(on)则从右往左找第一个非可信 IP —— 适用于你控制全部代理层的场景
替代方案:用 $http_x_real_ip 做一致性 hash(更灵活)
如果 ip_hash 因架构限制难以修复,可改用 hash 指令手动指定变量:
- 在 upstream 块中写:
hash $http_x_real_ip consistent;(或hash $remote_addr consistent;) - 注意:
consistent启用 ketama 一致性哈希,比原生ip_hash更抗节点增减抖动 - 前提仍是确保
$http_x_real_ip已被可信代理设置且未被污染;可在 proxy_pass 前用proxy_set_header X-Real-IP $remote_addr;主动透传











