1---2name: websocket3description: Implement reliable WebSocket connections with proper reconnection, heartbeats, and scaling.4---5
6## Reconnection (Always Forget)
7
8- Connections drop silently—TCP FIN may never arrive; don't assume `onclose` fires
9- Exponential backoff: 1s, 2s, 4s, 8s... cap at 30s—prevents thundering herd on server recovery
10- Add jitter: `delay * (0.5 + Math.random())`—prevents synchronized reconnection storms
11- Track reconnection state—queue messages during reconnect, replay after
12- Max retry limit then surface error to user—don't retry forever silently
13
14## Heartbeats (Critical)
15
16- Ping/pong frames at protocol level—browser doesn't expose; use application-level ping
17- Send ping every 30s, expect pong within 10s—no pong = connection dead, reconnect
18- Server should ping too—detects dead clients, cleans up resources
19- Idle timeout in proxies (60-120s typical)—heartbeat must be more frequent
20- Don't rely on TCP keepalive—too infrequent, not reliable through proxies
21
22## Connection State
23
24- `readyState`: 0=CONNECTING, 1=OPEN, 2=CLOSING, 3=CLOSED—check before sending
25- Buffer messages while CONNECTING—send after OPEN
26- `bufferedAmount` shows queued bytes—pause sending if backpressure building
27- Multiple tabs = multiple connections—coordinate via BroadcastChannel or SharedWorker
28
29## Authentication
30
31- Token in URL query: `wss://host/ws?token=xxx`—simple but logged in access logs
32- First message auth: connect, send token, wait for ack—cleaner but more round trips
33- Cookie auth: works if same origin—but no custom headers in WebSocket
34- Reauthenticate after reconnect—don't assume previous session valid
35
36## Scaling Challenges
37
38- WebSocket connections are stateful—can't round-robin between servers
39- Sticky sessions: route by client ID to same server—or use Redis pub/sub for broadcast
40- Each connection holds memory—thousands of connections = significant RAM
41- Graceful shutdown: send close frame, wait for clients to reconnect elsewhere
42
43## Nginx/Proxy Config
44
45```
46proxy_http_version 1.1;
47proxy_set_header Upgrade $http_upgrade;
48proxy_set_header Connection "upgrade";
49proxy_read_timeout 3600s;
50```
51- Without these headers, upgrade fails—connection closes immediately
52- `proxy_read_timeout` must exceed your ping interval—default 60s too short
53- Load balancer health checks: separate HTTP endpoint, not WebSocket
54
55## Close Codes
56
57- 1000: normal closure; 1001: going away (page close)
58- 1006: abnormal (no close frame received)—usually network issue
59- 1008: policy violation; 1011: server error
60- 4000-4999: application-defined—use for auth failure, rate limit, etc.
61- Always send close code and reason—helps debugging
62
63## Message Handling
64
65- Text frames for JSON; binary frames for blobs/protobuf—don't mix without framing
66- No guaranteed message boundaries in TCP—but WebSocket handles framing for you
67- Order preserved per connection—messages arrive in send order
68- Large messages may fragment—library handles reassembly; set max message size server-side
69
70## Security
71
72- Validate Origin header on handshake—prevent cross-site WebSocket hijacking
73- Same-origin policy doesn't apply—any page can connect to your WebSocket server
74- Rate limit per connection—one client can flood with messages
75- Validate every message—malicious clients can send anything after connecting
76
77## Common Mistakes
78
79- No heartbeat—connection appears alive but is dead; messages go nowhere
80- Reconnect without backoff—hammers server during outage, prolongs recovery
81- Storing state only in connection—lost on reconnect; persist critical state externally
82- Huge messages—blocks event loop; stream large data via chunking
83- Not handling `bufferedAmount`—memory grows unbounded if client slower than server