nginx 的 ip_hash 不支持自定义哈希源,需改用 hash 指令配合 map 预处理变量实现灵活会话保持;map 提取真实ip、用户id或cookie等键值,hash 基于该值做一致性哈希调度。

Nginx 的 ip_hash 本身是固定算法:对客户端 $remote_addr 做哈希,强制绑定到某台后端服务器,不支持自定义哈希源或逻辑。但如果你需要更灵活的会话保持策略(比如按真实 IP、特定请求头、Cookie 或用户 ID 分发),就不能直接用 ip_hash,而要改用 hash 指令配合 map 预处理变量——这才是“结合 map 实现自定义哈希”的正确路径。
为什么不能在 ip_hash 里用 map
ip_hash 是独立指令,只认 $remote_addr,不接受变量替换,也不支持 map 结果。它和 hash 是两个不同机制:
-
ip_hash:内置、固定、仅限 IPv4/IPv6 地址,自动剔除不可用节点(但不支持权重、backup); -
hash:通用哈希指令,可作用于任意字符串变量(包括map构造的值),支持一致性哈希(加consistent参数),也兼容weight和backup。
用 map + hash 实现自定义哈希分发
核心思路:先用 map 提取/构造哈希依据(如真实 IP、Token、User-ID),再在 upstream 中用 hash 对该变量做哈希调度。
提取真实客户端 IP(处理代理场景)
map $http_x_forwarded_for $real_ip {
~^(\d+\.\d+\.\d+\.\d+) $1;
default $remote_addr;
}
upstream backend_pool {
hash $real_ip consistent; # 启用一致性哈希,减少节点变动时的重散列
server 192.168.1.101:8080 weight=3;
server 192.168.1.102:8080;
server 192.168.1.103:8080 backup;
}
✅ 说明:
$http_x_forwarded_for可能含多个 IP(如X.X.X.X, Y.Y.Y.Y),正则~^(...)只取第一个,避免哈希错乱。
按登录用户 ID 维持会话(需前端透传)
假设前端在请求头中带 X-User-ID: abc123:
map $http_x_user_id $session_key {
"" $remote_addr; # 未登录时退化为 IP 哈希
default $http_x_user_id; # 已登录则用用户 ID
}
upstream backend_pool {
hash $session_key consistent;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
⚠️ 注意:
$http_x_user_id是小写形式(Nginx 自动转换 HTTP 头名),且需确保该头由可信上游设置,防止客户端伪造导致会话劫持。
按 Cookie 中的 session_id 哈希(无 Token 场景)
map $cookie_session_id $hash_key {
"" $remote_addr;
default $cookie_session_id;
}
upstream backend_pool {
hash $hash_key consistent;
server 192.168.1.101:8080;
server 192.168.1.102:8080;
}
✅ 优势:天然兼容传统 Web 应用,无需修改前端逻辑,只要 Cookie 存在就绑定;空值时自动 fallback 到 IP,保证基本会话连续性。
关键细节提醒
-
hash必须配合consistent才能真正实现“增减节点影响最小”,否则默认哈希易导致大量请求重映射; -
map块必须放在http级别,不能嵌套在server或location中; - 所有被
hash引用的变量,必须在map中明确定义默认分支(如""或default),否则未匹配时值为空,会导致哈希结果不稳定; - 若后端服务本身有 session 共享(如 Redis),其实不一定需要 Nginx 层哈希,此时应优先考虑无状态设计,而非强绑定。
不复杂但容易忽略。











