Introduction
WebSocket close code 1006 means the connection was closed abnormally without a proper close frame exchange. Unlike codes 1000 (normal) or 1001 (going away), code 1006 indicates the connection was interrupted - by a network failure, proxy timeout, server crash, or client crash. The application cannot distinguish between a clean close and a dropped connection without checking for this code.
Symptoms
WebSocket connection closed with code 1006unexpected server response: 502followed by 1006 close- Clients disconnect randomly without application-level reason
- Reconnection storms after network blip
- Load balancer dropping idle connections
ws.on('close', (code) => console.log(code))shows 1006
ws.on('close', (code, reason) => {
console.log(`WebSocket closed: code=${code}, reason=${reason.toString()}`);
// code=1006, reason=<Buffer > (empty - no reason provided)
// 1006 means "abnormal closure" - no close frame was received
});Common Causes
- Network interruption between client and server
- Load balancer or proxy closing idle connections
- Server process restart without sending close frame
- Client browser tab closed or navigated away
- WebSocket ping/pong not configured (connection appears dead)
- Firewall/NAT dropping long-lived connections
Step-by-Step Fix
- 1.Implement heartbeat with ping/pong:
- 2.```javascript
- 3.const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => { ws.isAlive = true;
ws.on('pong', () => { ws.isAlive = true; // Client responded });
ws.on('message', (data) => { // Handle messages }); });
// Check heartbeat every 30 seconds const interval = setInterval(() => { wss.clients.forEach((ws) => { if (ws.isAlive === false) { console.log('Client not responding to ping, terminating'); return ws.terminate(); // Force close } ws.isAlive = false; ws.ping(); // Send ping - expect pong response }); }, 30000);
wss.on('close', () => clearInterval(interval)); ```
- 1.Client-side reconnection with exponential backoff:
- 2.```javascript
- 3.function connectWithRetry(url, maxRetries = 10) {
- 4.let retries = 0;
function connect() { const ws = new WebSocket(url);
ws.on('open', () => { console.log('Connected'); retries = 0; // Reset on successful connection });
ws.on('close', (code) => {
console.log(Disconnected: code=${code});
if (code === 1006 || code === 1001) {
// Abnormal close or server going away - reconnect
if (retries < maxRetries) {
const delay = Math.min(1000 * Math.pow(2, retries), 30000);
const jitter = Math.random() * 1000;
console.log(Reconnecting in ${delay + jitter}ms (attempt ${retries + 1}));
setTimeout(() => { retries++; connect(); }, delay + jitter); } else { console.error('Max reconnection attempts reached'); } } });
ws.on('error', (err) => { console.error('WebSocket error:', err.message); });
return ws; }
return connect(); }
// Usage const ws = connectWithRetry('ws://localhost:8080'); ```
- 1.Configure proxy/load balancer timeouts:
- 2.```nginx
- 3.# Nginx configuration for WebSocket
- 4.location /ws {
- 5.proxy_pass http://backend;
- 6.proxy_http_version 1.1;
- 7.proxy_set_header Upgrade $http_upgrade;
- 8.proxy_set_header Connection "upgrade";
# Increase timeouts for long-lived WebSocket connections proxy_read_timeout 86400s; # 24 hours proxy_send_timeout 86400s; proxy_connect_timeout 60s;
# Send periodic proxy-level pings proxy_socket_keepalive on; } ```
- 1.Handle 1006 in application logic:
- 2.```javascript
- 3.class WebSocketClient {
- 4.constructor(url) {
- 5.this.url = url;
- 6.this.ws = null;
- 7.this.listeners = new Map();
- 8.this.connect();
- 9.}
connect() { this.ws = new WebSocket(this.url);
this.ws.on('open', () => { this.emit('open'); });
this.ws.on('message', (data) => { this.emit('message', data); });
this.ws.on('close', (code, reason) => { this.emit('close', code, reason);
// 1006 = abnormal - likely network issue if (code === 1006) { console.warn('Abnormal closure - possible network issue'); } });
this.ws.on('error', (err) => { this.emit('error', err); }); }
on(event, callback) { if (!this.listeners.has(event)) { this.listeners.set(event, []); } this.listeners.get(event).push(callback); }
emit(event, ...args) { const callbacks = this.listeners.get(event) || []; callbacks.forEach(cb => cb(...args)); } } ```
Prevention
- Always implement ping/pong heartbeat on the server
- Use exponential backoff with jitter for reconnection
- Configure load balancer and proxy timeouts for WebSocket connections
- Monitor WebSocket connection count and drop rate in production
- Set
clientTracking: trueon ws.Server to track connected clients - Use
ws.terminate()instead ofws.close()for unresponsive clients - Add connection-level logging to diagnose 1006 patterns
- In Kubernetes, configure
readinessProbeandlivenessProbeseparately from WebSocket health