ip_hash通过客户端真实ip哈希实现请求路由固化,天然匹配websocket“单连接、长存活”特性;但依赖nginx获取真实ip,需real_ip_module还原,且不支持weight、backup,故障无自动迁移,ipv6哈希易偏斜。

ip_hash 能解决 WebSocket 的 Session 粘滞问题,但仅在特定网络条件下可靠;它不是万能方案,用错场景反而会引发连接集中、故障无迁移、IPv6 偏斜等线上事故。
为什么 ip_hash 对 WebSocket 有效?
WebSocket 连接建立后是长生命周期的 TCP 连接,后续所有帧(ping/pong/数据)必须路由到同一台后端,否则因无会话上下文而断连。ip_hash 正是靠客户端真实 IP 哈希值固定映射后端,天然匹配这种“单连接、长存活”特性。
但它依赖一个前提:Nginx 必须拿到真实的客户端 IP。如果前端有 CDN、WAF、云 LB 或 NAT 网关,$remote_addr 就是代理 IP,所有请求哈希结果一致,全部打到同一台后端——这不是粘滞,是单点雪崩。
- 必须配合
real_ip_module+set_real_ip_from还原源 IP - 确认 CDN/WAF 已在
X-Forwarded-For或X-Real-IP中透传原始 IP - 检查 Nginx 日志中
$http_x_forwarded_for和$remote_addr是否符合预期
ip_hash 的配置陷阱与强制约束
ip_hash 不是加在 server 或 location 里,它只能出现在 upstream 块顶层,且一旦启用,就自动禁用 weight、backup、max_fails 等指令——这些配置共存会直接导致 Nginx 启动失败。
典型错误配置:
upstream ws_backend {
ip_hash;
server 10.0.0.1:8080 weight=2; # ← 报错:weight 与 ip_hash 冲突
server 10.0.0.2:8080 backup; # ← 报错:backup 与 ip_hash 冲突
}
正确写法(无权重、无备用节点):
upstream ws_backend {
ip_hash;
server 10.0.0.1:8080;
server 10.0.0.2:8080;
server 10.0.0.3:8080;
}
-
ip_hash无法感知后端健康状态:某台服务器宕机,对应 IP 段的哈希槽直接失效,客户端重连时仍被哈希到故障节点,直到超时失败才轮到下一台 - IPv6 地址哈希分布不均,部分节点连接数远高于其他节点(实测常见于 /64 子网内大量地址哈希碰撞)
- 企业内网、校园网、4G/5G 移动出口常为 NAT 共享 IP,几十甚至上百用户被绑定到同一后端,极易过载
WebSocket 专用代理参数不能少
只配 ip_hash 不够,Nginx 必须正确处理协议升级和长连接维持。以下参数缺一不可:
-
proxy_http_version 1.1:强制 HTTP/1.1,否则无法支持Upgrade头 -
proxy_set_header Upgrade $http_upgrade:透传客户端发起的升级请求 -
proxy_set_header Connection $connection_upgrade:其中$connection_upgrade需提前用map定义,不能硬写"upgrade" -
proxy_read_timeout和proxy_send_timeout建议设为 3600~86400(1h~24h),避免空闲连接被主动关闭 -
proxy_buffering off:禁用缓冲,防止 WebSocket 帧被合并或延迟发送
漏掉 map 定义是高频错误:
http {
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream ws_backend { ip_hash; ... }
server {
location /ws/ {
proxy_pass http://ws_backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade; # ← 依赖上面的 map
proxy_read_timeout 3600;
proxy_buffering off;
}
}
}
什么情况下该放弃 ip_hash?
当你的业务已接入 CDN、面向全国移动用户、或部署在混合云/NAT 环境中,ip_hash 的可靠性急剧下降。此时应切换为基于 Cookie 的粘滞方案,例如:
- Nginx 商业版的
sticky cookie指令(需付费) - 开源方案:后端在 WebSocket 握手成功后 Set-Cookie 返回
route=backend-1,Nginx 用sticky cookie route实现路由 - 更彻底的解法:后端改用无状态设计 + Redis 存储连接元数据,彻底摆脱粘滞依赖
真正容易被忽略的是:Nginx 的 ip_hash 是按 IPv4 地址前 3 段哈希,IPv6 则取整个地址做哈希——这意味着同一个 /64 子网下的不同设备,在 IPv4 下可能被分到不同节点,但在 IPv6 下却大概率落在同一节点,造成隐性负载倾斜。










