The Problem

WebSocket close code 1006 means the connection was closed abnormally without a proper close frame. This happens due to network interruptions, server restarts, proxy timeouts, or load balancer issues.

Symptoms

  • WebSocket disconnects randomly without warning
  • Real-time features stop working (chat, notifications, live updates)
  • No close event data is available
  • Reconnecting sometimes works, sometimes fails

Real Error Event

javascript
ws.onclose = (event) => {
  console.log('Code:', event.code);     // 1006
  console.log('Reason:', event.reason); // "" (empty)
  console.log('WasClean:', event.wasClean); // false
};

Common Causes

Cause 1: Proxy Timeout (Nginx)

nginx
# Default proxy_read_timeout is 60 seconds
# If no data is sent for 60s, nginx closes the connection

Cause 2: Load Balancer Idle Timeout

AWS ALB has a default idle timeout of 60 seconds. If no data flows, it closes the connection.

Cause 3: Server Restart or Crash

When the server restarts, all WebSocket connections are dropped with code 1006.

How to Fix It

Fix 1: Heartbeat/Ping-Pong

```javascript // Client function createWebSocket(url) { const ws = new WebSocket(url); let pingInterval;

ws.onopen = () => { // Send ping every 30 seconds pingInterval = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send(JSON.stringify({ type: 'ping' })); } }, 30000); };

ws.onclose = () => { clearInterval(pingInterval); // Reconnect after 3 seconds setTimeout(() => createWebSocket(url), 3000); };

return ws; } ```

```javascript // Server (Node.js with ws library) wss.on('connection', (ws) => { ws.isAlive = true;

ws.on('pong', () => { ws.isAlive = true; });

ws.on('message', (data) => { const msg = JSON.parse(data); if (msg.type === 'ping') { ws.send(JSON.stringify({ type: 'pong' })); } }); });

// Server-side ping check setInterval(() => { wss.clients.forEach(ws => { if (!ws.isAlive) return ws.terminate(); ws.isAlive = false; ws.ping(); }); }, 30000); ```

Fix 2: Nginx WebSocket Configuration

nginx
location /ws {
  proxy_pass http://backend;
  proxy_http_version 1.1;
  proxy_set_header Upgrade $http_upgrade;
  proxy_set_header Connection "upgrade";
  proxy_read_timeout 86400s;  # 24 hours
  proxy_send_timeout 86400s;
}

Fix 3: Exponential Backoff Reconnection

```javascript class ReconnectingWebSocket { constructor(url) { this.url = url; this.reconnectAttempts = 0; this.maxReconnectAttempts = 10; this.connect(); }

connect() { this.ws = new WebSocket(this.url); this.ws.onclose = () => { if (this.reconnectAttempts < this.maxReconnectAttempts) { const delay = Math.min(1000 * Math.pow(2, this.reconnectAttempts), 30000); this.reconnectAttempts++; setTimeout(() => this.connect(), delay); } }; this.ws.onopen = () => { this.reconnectAttempts = 0; // Reset on successful connection }; } } ```

Fix 4: Handle Browser Tab Visibility

javascript
document.addEventListener('visibilitychange', () => {
  if (!document.hidden && ws.readyState !== WebSocket.OPEN) {
    ws = new WebSocket(url); // Reconnect when tab becomes visible
  }
});