# Seven WebSocket Mistakes That Destroy Production Reliability

> Avoid the most damaging WebSocket production mistakes: missing heartbeats, no reconnect logic, unguarded broadcasts, and five more critical pitfalls explained.

- **Published:** 2025-04-09
- **Author:** Clixo
- **Reading time:** 5 min read
- **Tags:** websockets, production, reliability, common-mistakes, real-time
- **Canonical URL:** https://clixo.sh/blog/websocket-production-mistakes

Most WebSocket bugs do not surface in development. They appear under load, after network interruptions, or six weeks into production when a few specific edge cases have finally been triggered. By then, the damage is already visible to users. These are the seven mistakes most teams make, and how to avoid each one.

## Mistake 1: No Heartbeat Mechanism

TCP does not always surface dead connections promptly. A mobile client can go offline, lose signal, or have a middlebox drop the connection — and the server continues to believe that socket is alive. Without a heartbeat, your connection count climbs indefinitely as dead sockets accumulate, your presence system reports phantom users as online, and memory leaks.

**The fix:** Implement a ping/pong cycle. Send a ping every 15 to 30 seconds. If the client has not responded with a pong before the next cycle, terminate the connection. The WebSocket protocol has native ping/pong frames; use them rather than building application-level heartbeats.

## Mistake 2: No Client-Side Reconnection Logic

WebSocket connections drop. Network conditions change, servers restart, load balancers cycle. If your client makes one connection attempt and treats a close event as a terminal failure, your users see a broken real-time feature until they manually refresh the page.

**The fix:** Implement reconnection with exponential backoff. On close, wait an increasing interval before reconnecting — 500ms, 1s, 2s, 4s, up to a ceiling. Add a small random jitter to prevent thundering herd when many clients disconnect simultaneously and attempt reconnection at the same moment.

```js
let delay = 500;
function reconnect() {
  setTimeout(() => {
    const socket = new WebSocket(WS_URL);
    setupHandlers(socket);
  }, delay + Math.random() * 200);
  delay = Math.min(delay * 2, 30000);
}
```

## Mistake 3: Broadcasting to All Clients Without Scoping

Every time an event occurs, broadcasting it to every connected client is the fastest way to DoS your own server at scale. If you have ten thousand connections and broadcast a message every time any user performs any action, you are generating up to ten thousand send operations per event.

**The fix:** Scope broadcasts to rooms, channels, or subscription lists. A user editing document A does not need to receive events from document B. Design your subscription model before you write broadcast code. Group sockets by room ID in a `Map` and broadcast only within the relevant group.

## Mistake 4: Skipping Authentication on the Socket

The WebSocket handshake starts as an HTTP Upgrade request, so standard cookie-based session validation can work — but it is often skipped. Developers assume that "only authenticated users can reach this page" is sufficient, which is not true when your WebSocket endpoint is a separate server or service.

**The fix:** Validate identity as the first action after a connection is opened. Accept a short-lived token in the first message (the "auth frame" pattern), verify it server-side, and close unauthenticated connections immediately with a 4001 close code before you add them to any room or presence map.

```mermaid
sequenceDiagram
  participant C as Client
  participant S as Server
  C->>S: WebSocket connect
  S->>S: Start 5s auth timeout
  C->>S: auth frame (JWT token)
  S->>S: Verify token
  S->>C: auth_ok (userId confirmed)
  C->>S: join room:42
  S->>C: messages for room:42
  Note over C,S: Unauthenticated sockets closed at timeout
```

## Mistake 5: Not Handling the Close Event on the Server

When a client disconnects, the server's `close` event fires on the socket. If you do not clean up — removing the socket from room maps, presence registries, and connection indices — you accumulate stale references. These stale references cause memory leaks, phantom presence, and attempts to write to closed sockets that generate cascading errors in your logs.

**The fix:** In your `close` handler, always remove the socket from every data structure it was added to. If the socket was not authenticated (it closed before the auth frame arrived), ensure your handler checks for that case before attempting to dereference `socket.userId`.

## Mistake 6: Sending Large Payloads on Every Event

WebSocket frames can carry arbitrary payloads, which tempts developers to send entire application state on every change. In a collaborative document, this means broadcasting the full document on every keystroke. At even modest document sizes and user counts, this saturates bandwidth and slows client rendering.

**The fix:** Send deltas, not snapshots. Design your message schema to describe what changed: the operation, the affected entity, and the minimum data required to apply the change on the client. Reserve full-state syncs for initial connection and explicit resync requests.

## Mistake 7: Ignoring Message Ordering and Idempotency

Clients reconnect. When they do, they miss messages sent during the disconnection window. If your application assumes every client has received every message in order, reconnecting clients will have stale or incomplete state.

**The fix:** Assign a monotonic sequence number to every message your server sends. When a client reconnects, it sends its last received sequence number. The server replays missed messages or sends a full resync payload. This requires your message store to be queryable by sequence — a Redis stream or an append-only database table both work. Design this from the start; retrofitting it is painful.

---

These mistakes compound. A server with no heartbeat, no room scoping, and no authentication cleanup can become unreliable within days of deployment at scale. Building the correct primitives early costs less than debugging production incidents later.

If you are building a real-time product and want the WebSocket layer designed correctly from day one, [talk to Clixo](https://clixo.sh/#contact). We design and ship real-time systems as a core product capability.

---

Clixo · 1141 W Bryn Mawr Ave, Itasca, IL 60143, US · [hello@clixo.sh](mailto:hello@clixo.sh)
[Start a build](https://clixo.sh/#contact) · [All services](https://clixo.sh/services) · [Agent guide (llms.txt)](https://clixo.sh/llms.txt)
