ip_hash在nat环境下必然导致负载倾斜,因其将局域网出口ip视为单一客户端;须用real_ip_module还原真实ip,或改用cookie/token哈希并配合redis共享session。

ip_hash 会把整个局域网的出口 IP 当作一个客户端来哈希,所有用户请求全打到同一台后端,其余节点闲置——这不是配置错误,是机制本身在 NAT 场景下的必然结果。
真实 IP 没还原,ip_hash 就等于失效
企业、校园、运营商网络中,成百上千终端共用一个公网出口 IP(比如 203.101.5.8),Nginx 默认拿到的 $remote_addr 就是这个出口地址。ip_hash 对它哈希后,永远只映射到 upstream 中某一台 server。
- 必须启用
real_ip_module,在 http 或 server 块中配置set_real_ip_from指定可信内网网段(如172.16.0.0/12) - 设置
real_ip_header X-Forwarded-For(或实际透传头),并开启real_ip_recursive on防伪造 - 用
log_format同时记录$remote_addr和$http_x_forwarded_for,确认还原是否准确
IP 不具备区分度,就别硬绑 IP
即使还原出内网 IP,若局域网本身也做二层 NAT(比如办公网再套一层防火墙),$binary_remote_addr 仍可能大量重复。此时应放弃 IP 绑定逻辑:
-
hash $cookie_JSESSIONID:适用于 Java 应用已生成有效 session ID 的场景 -
hash $arg_token或hash $http_authorization:对接口级鉴权更稳定,token 天然唯一 -
consistent_hash $binary_remote_addr:需较新 Nginx 版本,增减后端时仅影响少量连接
后端必须能共享状态,否则换策略也没用
无论用哪种 hash 方式,只要应用依赖本地内存存 session、用户上下文或 WebSocket 连接,单点故障或扩容就会断连。必须配套改造:
- 引入 Redis 或其他集中式 Session 存储,让所有后端读写同一份会话数据
- 改用 JWT 等无状态认证,彻底解除对服务端存储的依赖
- WebSocket 场景可结合 STOMP + 消息中间件广播事件,避免强绑定
别让 ip_hash 承担它不该扛的责任
ip_hash 不是通用会话保持方案,只适合真实 IP 稳定、无代理、无 NAT、后端规模固定的简单场景。如果业务已用 Redis 共享 Session,或者接口走 token 鉴权,直接关掉 ip_hash,改用 least_conn 或加权轮询反而更健壮。











