要让 nginx 稳定代理 websocket 并做负载均衡,关键在于会话保持、协议透传和超时适配三者协同:需用 ip_hash/sticky cookie/hash $http_sec_websocket_key 实现连接绑定;显式配置 proxy_http_version 1.1、proxy_set_header upgrade $http_upgrade 和 connection "upgrade";设置 proxy_read_timeout、proxy_send_timeout 为长值并禁用缓冲;location 需精确匹配且校验 $http_upgrade。

要让 Nginx 稳定代理 WebSocket 并做负载均衡,关键不是“能不能转发”,而是确保连接不被拆散、不被中断、不被误路由——这需要会话保持 + 协议透传 + 超时适配三者协同生效。
必须开启连接绑定,避免帧乱发
WebSocket 是单次握手、长期复用的 TCP 连接,后端通常把用户状态(如登录态、房间信息、心跳计时)存在本地内存里。若 Nginx 用默认轮询,同一客户端的后续 ping 帧或消息可能打到不同后端,导致连接拒绝、消息丢失、重复登录。
- ip_hash:最简方案,按客户端 IP 哈希固定后端;适合直连、IP 分散场景,但 NAT 或 CDN 后所有用户 IP 相同,会压垮单台机器
-
sticky cookie:后端在握手成功后 Set-Cookie(如
ws_route=server-2),Nginx 用sticky cookie ws_route expires=1h实现路由;兼容移动端、动态 IP 和代理环境 - hash $http_sec_websocket_key:利用每次握手唯一的 Sec-WebSocket-Key 做哈希,保证一次连接的所有帧落在同一节点(注意:该值每次重连都变,不跨连接复用)
协议升级头不能漏,也不能写错
WebSocket 握手依赖两个逐跳头:Upgrade: websocket 和 Connection: upgrade。Nginx 默认不透传它们,且大小写、变量写法稍有偏差就会失败。
- 必须显式配置
proxy_http_version 1.1(HTTP/1.0 不支持 Upgrade) -
proxy_set_header Upgrade $http_upgrade—— 变量名必须小写,$HTTP_UPGRADE无效 -
proxy_set_header Connection "upgrade"—— 必须是字面量"upgrade",不能写成$http_connection(客户端可能是 keep-alive,会破坏握手) - 更健壮做法:用
map预处理,如map $http_upgrade $connection_upgrade { default upgrade; '' close; },再设proxy_set_header Connection $connection_upgrade
超时与缓冲设置直接影响连接寿命
Nginx 默认策略为短连接设计,对长连接极不友好。空闲超时、响应缓冲、连接复用等参数若未调优,会导致静默断连(如报错 1006、Unexpected response code: 200)。
-
proxy_read_timeout 86400(24 小时):防止空闲期被主动关闭;金融行情、IM 等低频推送场景尤其关键 -
proxy_send_timeout 86400:保障服务端心跳或广播能及时发出 -
proxy_buffering off:禁用缓冲,避免帧堆积、粘包或延迟触发心跳超时 -
upstream中启用keepalive 32:减少后端建连开销,提升绑定稳定性
location 匹配要精确,避免规则干扰
会话绑定只在匹配到对应 location 时才生效。宽泛的 location / { } 容易把 WebSocket 请求误判为普通 HTTP,导致头没透传、超时没生效。
- 使用明确路径,如
location /ws/或location ^~ /api/v1/ws - 加校验逻辑防误入:
if ($http_upgrade != "websocket") { return 403; } - 确保
proxy_pass地址协议与后端一致(如后端监听http://,就别写https://)











