← 中文资源

简体中文版本

WebSocket 1006 异常关闭 — 代理升级与空闲超时

手工发起 Upgrade 握手,检查代理头、空闲超时与服务端崩溃,配合心跳和退避重连解决 WebSocket 1006 断连。

阅读英文原文

现象

WebSocket 以代码 1006 关闭且没有关闭原因;浏览器控制台显示 Error during WebSocket handshake: Unexpected response code: 502;空闲约 60 秒后连接断开;直连应用正常,经过 nginx 或负载均衡就失败。

诊断

先手工发起升级请求并查看代理日志:

curl -i -N -H "Connection: Upgrade" -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: $(openssl rand -base64 16)" \
  https://example.com/ws
websocat -v wss://example.com/ws
tail -n 100 /var/log/nginx/error.log | grep -E 'upstream|timed out|closed'

返回 101 Switching Protocols 说明代理与应用都接受升级;400、403 或 502 则指出是哪一层在帧交换前拒绝。websocat 能显示真实的关闭帧,1006 表示 TCP 连接在意料之外结束,没有收到关闭帧。若断开间隔固定,几乎可以确定是代理的 proxy_read_timeout;间隔随机则要查服务重启、内存压力或网络。

修复

让代理使用 HTTP/1.1 并转发 Upgrade 头,读超时设为大于心跳周期:

location /ws {
    proxy_pass http://ws_backend;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 3600s;
}

应用侧每 20 到 30 秒发送 ping,超时未收到 pong 即判定连接失效;客户端使用带抖动的指数退避重连,并按消息 ID 续传,避免重复处理。不要为让连接成功而关闭 Origin 校验,那会引入跨站 WebSocket 劫持风险。

验证

确认握手返回 101,并让连接空闲超过原先的超时时间,依靠心跳保持不断;测试期间 nginx 与应用日志不应出现 upstream timeout 或崩溃。再重启一个后端实例,客户端应只重连一次并恢复正常,而不是出现 1006 风暴或重复消费。