A WebSocket connection that survives a coding demo is not the same as one that survives production traffic. In a demo, the network is stable, the tab stays open, and nobody’s phone drops from Wi-Fi to LTE mid-request. In production, none of that holds, and the difference between a websocket implementation that works and one that quietly loses data comes down to three things: how it reconnects, how it detects a dead connection, and how it recovers whatever happened while disconnected.
This article works through all three, plus the surrounding websocket architecture decisions that make them possible — starting with why persistent connections need this handling at all, and ending with the reconnect, heartbeat, and recovery patterns behind most production-grade websocket best practices in use today.
Most of it applies to any real-time system, from chat apps to stock tickers. The examples lean toward blockchain data — new blocks, pending transactions, live prices — because that’s where a missed event has an unusually clear cost: a trade that fires a second late, or a balance update nobody saw.
Why Do Persistent Connections Need Extra Handling in Production?

A WebSocket connection stays open for as long as both sides let it, which is exactly what makes it useful and exactly what makes it fragile. Routers reboot, phones switch networks, and load balancers close anything that’s gone quiet too long — a stateless HTTP request doesn’t care, but a multi-hour connection to a websocket server has to assume every drop is inevitable, not exceptional.
Here’s the part that catches people off guard: most drops are silent. The TCP socket can stay technically open on one side while the other side is long gone, especially after a mobile handoff or an ungraceful process restart. The client still thinks it’s subscribed, so the missed messages just never arrive.
On a blockchain network, that broadcast usually starts at a validator node or a relaying full node — the WebSocket layer’s only job is getting it to your application intact. Infrastructure makes that job harder before it makes it easier. Every proxy, load balancer, and CDN edge in the path enforces its own idle timeout, and they rarely agree with each other:
| Layer | Typical idle timeout | Recommended heartbeat interval |
|---|---|---|
| Nginx (default config) | 60 seconds | 45 seconds |
| AWS Application Load Balancer | 60 seconds | 45 seconds |
| Cloudflare | ~100 seconds | 75 seconds |
| Google Cloud Load Balancer | 30 seconds | 22 seconds |
That recommended interval follows a simple rule from WebSocket.org’s heartbeat guide: set it to roughly 75% of the shortest timeout in the path. Wait any longer, and a proxy closes the connection before the heartbeat notices anything is wrong.
The websocket diagram below traces the full lifecycle a production client actually has to manage, from the initial handshake through a drop and back:
CLIENT SERVER
|-- HTTP Upgrade request ----------->|
|<-- 101 Switching Protocols ---------|
|-- subscribe (e.g. new blocks) ----->|
|<-- event, event, event --------------|
|-- ping ------------------------------>| (every N seconds)
|<-- pong -------------------------------|
| ...connection drops... |
|-- wait 500ms, 1s, 2s... (jitter) -->|
|-- reconnect + resume(last_seq) ----->|
|<-- replay of missed events -----------|
Each arrow maps to one of the three problems covered here: ping/pong is the heartbeat, the backoff wait is the reconnect logic, and the replay step is missed-event recovery.
Who Actually Depends on a Persistent Connection?
Any application that needs to react the moment something happens, not a few seconds later, ends up building on WebSockets instead of repeated requests. On the blockchain side, that includes wallets watching for an incoming payment, dashboards tracking new blocks, and monitoring tools that alert on address activity.
Trading systems push this hardest. A Solana sniper bot catching a token launch within a single ~400-millisecond slot has no room for a five-second polling delay, and a copy-trading platform mirroring another trader’s position needs to see that trade the instant it lands. For both, a missed event isn’t a display glitch — it’s a missed trade.
These same websockets best practices show up in less latency-sensitive but higher-stakes places, too:
- Wallets and exchanges — detecting incoming deposits or balance changes without a manual refresh.
- Block explorers and analytics dashboards — streaming new blocks and transactions as they’re confirmed.
- DeFi protocols and liquidation bots — watching price feeds and collateral ratios to act before a position goes underwater.
- Chat, gaming, and collaboration tools — the original use case, and still the easiest to reason about, since a missed message is rarely catastrophic.
A dropped chat message is a minor inconvenience; a dropped liquidation alert or missed trade signal has a dollar value attached. The stricter that cost, the more the reconnect and recovery logic below actually matters.
How Should a Client Reconnect After a Drop?
The direct answer: exponential backoff, randomized jitter, and a hard limit on how long it keeps trying. Reconnecting instantly and repeatedly is one of the most common mistakes in a naive websocket implementation, and it tends to fail right when it matters most — just after an outage, when the server is already struggling to recover.
The mechanics are well established. WebSocket.org’s reconnection guide lays out a formula that shows up, in some form, in most production clients:
- Start with a base delay — 500ms is typical.
- Double the delay after each failed attempt.
- Cap the delay at a maximum, commonly 30 seconds, so retries don’t stretch out indefinitely.
- Apply jitter by randomizing each delay to somewhere between 50% and 100% of the calculated value.
- Stop after a set number of attempts — usually 10 to 15 — or a set amount of elapsed time, usually 2 to 5 minutes.
Step four is the one people skip, and the one that matters most at scale. Matthew O’Riordan, CEO and co-founder of the realtime infrastructure company Ably, puts it plainly in WebSocket.org’s reconnection guide: “Without jitter, all clients retry at exactly the same intervals — 500ms, 1s, 2s, 4s — and every retry wave hits the recovering server simultaneously.” A server that just came back online gets hit by a synchronized wave of attempts instead of a smooth trickle, which can knock it back down.
The retry cap matters just as much, even though giving up feels counterintuitive. A client that retries forever against a genuinely dead server just burns battery and adds log noise. It’s better to stop after a few minutes and surface a clear “disconnected” state, letting a higher-level process decide whether to keep trying.
How Do Heartbeats Prevent Silent Connection Failures?
A heartbeat is a small message sent at a regular interval purely to confirm the other side is still there. Without one, a client or server has no reliable way to tell a slow connection from a dead one — both look identical until you try to use them.
WebSocket.org’s heartbeat guide gives this failure mode a name worth knowing: a zombie connection, defined as “a connection where the TCP socket is open but the remote peer is unreachable” — the kind of state that follows a network partition, a phone’s radio dropping to save battery, or a crash that skips a proper close frame. The socket looks fine. It just isn’t.
There are two ways to implement this, and they trade off differently. The WebSocket protocol itself defines ping and pong control frames — opcodes 0x9 and 0xA in RFC 6455 — adding only about 2 bytes of overhead per check. The catch: the WebSocket API gives browser JavaScript no way to send or detect one, so browser apps fall back to an application-level heartbeat instead — a plain message like {"type":"ping"}, at roughly 15 to 20 bytes instead of 2.
That gap barely matters for one connection and adds up fast at scale — a server holding 100,000 open connections sends noticeably more heartbeat bytes with JSON than with protocol frames, purely from the overhead difference. Native mobile and backend clients aren’t boxed in by a browser API, so they can use protocol-level pings directly and skip the extra bytes.
One more layer sits below both of these: OS-level TCP keepalive. It sounds like it should solve this outright, but Linux’s default TCP_KEEPIDLE is 7,200 seconds — two hours — far too slow to catch anything before an application-level heartbeat already would. Treat it as a backstop, not a strategy.
How Do You Recover Events Missed During a Disconnect?

The fix is a replay mechanism: the server tags every event with an increasing sequence number, and the client asks for everything after the last one it received. Skip this step and reconnect-and-heartbeat logic only solves part of the problem — whatever happened during the gap is otherwise just gone.
Both pieces have to work together. The server assigns that sequence number or timestamp to every event and keeps a short buffer of recent history, typically a window of a few minutes. On reconnect, the client sends back the last marker it saw, and the server replays everything after it.
Blockchain data has an advantage a lot of real-time systems don’t: it’s already ordered and re-fetchable. A block has a height, a transaction has a position within it, and none of it disappears afterward. A well-built client can just ask a standard JSON-RPC endpoint for the current block height, compare it to the last block it saw over the WebSocket feed, and backfill the gap before resuming the live subscription.
That backfill step is where a WebSocket feed and a standard RPC connection work together rather than replacing each other. NOWNodes, for instance, lists WebSocket access on 30-plus of its supported networks, with subscriptions covering new blocks, transactions, and address activity depending on the chain — and the same account’s RPC endpoint reconciles state after a gap, since the push feed alone has no memory of what it missed.
Session identity ties this together server-side. A common pattern issues a session ID on first connection, which the client stores and presents on reconnect, letting the server route it back to any buffered state tied to that ID — typically valid for two to five minutes before a full resync becomes the only option.
WebSocket vs. Polling vs. Server-Sent Events: Which Should You Use?
Not every real-time problem needs a full websocket server. The right choice depends on whether data needs to travel in one direction or two, and how much latency is actually acceptable.
| Factor | WebSocket | HTTP Polling | Server-Sent Events (SSE) |
|---|---|---|---|
| Direction | Full-duplex — both sides send | Client-initiated only | Server-to-client only |
| Latency | Sub-second, pushed immediately | Bound by poll interval | Sub-second, pushed immediately |
| Reconnect handling | Must be built by the developer | None needed — stateless | Built into the browser’s EventSource |
| Infrastructure complexity | Higher — persistent connections, sticky routing | Lowest | Moderate |
| Good fit | Trading feeds, live order books, chat | Low-frequency checks, simple dashboards | One-way notifications, log streams |
Polling still wins for anything that doesn’t need sub-second freshness — a dashboard refreshing once a minute doesn’t need a persistent connection’s overhead. SSE is worth a look whenever data only flows one way, since browsers already handle its reconnect logic automatically, removing an entire category of the bugs covered above.
WebSockets earn their complexity when data has to move both ways, or when even a fast poll is too slow — a live order book or a bidirectional trading terminal genuinely needs one. The websocket architecture covered here — heartbeats, backoff, sequence-based recovery — is the cost of admission for that channel: a fair trade when the use case demands it, unnecessary weight when it doesn’t.
Conclusion
Reconnects, heartbeats, and missed-event recovery aren’t three separate features — they’re three views of the same underlying problem: a persistent connection will eventually break, and the system needs to notice, recover, and not lose data while it does. Skip any one of the three and the other two only partially cover for it.
The practical takeaway: treat all three as required from the start, not something to patch in after a production incident. Build the heartbeat first so you can detect a dead connection, add backoff with jitter so reconnects don’t create their own outage, and give the server a way to replay what a client missed. None of it is exotic engineering — just a handful of well-documented patterns, applied consistently.
Get those three right, and the rest of a real-time application — trading logic, notification delivery, live dashboards — gets to assume the data underneath it is complete. That assumption is worth the upfront work.
FAQ
How many times should a WebSocket client retry a failed connection before giving up?
Most production clients cap retries at 10 to 15 attempts, or 2 to 5 minutes of elapsed time, whichever comes first. Retrying indefinitely against a genuinely dead server wastes battery and resources without improving the odds of reconnecting.
What do WebSocket close codes actually tell you?
A close code explains why a connection ended: 1000 means a clean, intentional close, while 1006 (abnormal closure) or 1011 (server error) signal something went wrong. Checking the code before reconnecting stops a client from endlessly retrying a connection the server closed on purpose.
Does every blockchain network support WebSocket subscriptions?
No. WebSocket access typically covers fewer networks than standard RPC access — on NOWNodes, for example, WebSocket is available on 30-plus networks against a broader 120-plus supported through RPC. Confirm WebSocket support for a specific chain before building a subscription feature around it.
Do mobile clients need different reconnect handling than desktop apps?
Generally, yes. Mobile connections drop far more often — switching between Wi-Fi and cellular, or the OS suspending a backgrounded app — so mobile clients typically need shorter heartbeat intervals and reconnect logic that resumes the moment the app returns to the foreground, rather than waiting for the next check-in.



