What is a heartbeat — and why an "open" connection can be long dead

You built a chat on WebSocket. Everything flies. Then a tester gets on the subway, loses signal for a minute, comes out — and the chat is silent. No error, no "reconnecting". The socket on her phone thinks it's still connected.
Here's the tricky part. When a connection drops without a goodbye — the phone went into a tunnel, the laptop lid closed — nobody sends an "I've disconnected" signal. Both sides just stop hearing each other. And each one thinks the other is quiet because it has nothing to say.
TCP's built-in "are you alive?" check on Linux kicks in by default only after two hours of silence. For a chat that's forever. So apps check the connection themselves. That's a heartbeat.
What a heartbeat is
A heartbeat is a tiny service message that one side regularly sends the other to say: "I'm alive, the link is up".
Like a doctor taking a pulse. What matters isn't the beat itself but that it arrives regularly. Miss several beats in a row — time to worry.
The message usually carries nothing. No data, no meaning. Its value is the fact that it arrived, and when.
How it works
The scheme is almost always the same.
- Every N seconds the client sends a "ping".
- The server replies "pong".
- Each side remembers when it last heard anything.
- If the silence lasts longer than a threshold, the connection is considered dead. It's closed and reopened.
The threshold has a margin: usually 2–3 intervals. One missed ping might just be a slow network. Three in a row isn't.
Replay that subway ride second by second. At 0 and 25 pings go out, pongs come back. At 30 the train enters a tunnel. The ping at 50 goes nowhere. The ping at 75 too, and the silence is now longer than 75 seconds. The client closes the socket, shows "No connection" and starts reconnecting. Signal returns at 90 — a couple of seconds later the chat is alive again. Without a heartbeat the same phone would sit "online" for up to two hours.
Who should ping — the client or the server? It doesn't matter, as long as both sides watch for silence. The server wants to know the client is gone too: otherwise it holds memory for thousands of ghost connections and sends them messages for nothing.
Here's how that looks in your code:
let lastSeen = Date.now();
socket.onmessage = () => { lastSeen = Date.now(); };
setInterval(() => {
if (Date.now() - lastSeen > 75_000) {
socket.close(); // silent for more than three intervals
reconnect();
} else {
socket.send(JSON.stringify({ type: "ping" }));
}
}, 25_000);
Where you've already met it without noticing:
- Supabase Realtime. By default the client sends a heartbeat every 25 seconds and the server replies. You change the interval with the
heartbeatIntervalMsoption increateClient. - Socket.IO. The server pings the client itself; the interval and timeout live in
pingIntervalandpingTimeout.
Why you need it
A heartbeat solves two problems at once — and both bite beginners.
First — catch a dead connection. Without a pulse your UI shows "online" while messages vanish into the void. With one, you know the truth within a minute and can show "No connection, reconnecting…".
Second — keep the connection from dying. Between you and the server sit proxies and load balancers. They close connections where nothing has moved for a while. nginx, for example, has proxy_read_timeout at 60 seconds by default. A chat where nobody types for a minute will drop on its own. A ping every 25 seconds keeps the channel "warm".
The same idea lives outside sockets. A background worker writes "I'm alive" to a table once a minute. A cron job checks whether that mark has frozen. If it has, you get an alert instead of a user complaint.
One more detail people forget. Caught a drop — don't reconnect instantly in a loop. If the server went down, a thousand clients hammering it every 100 milliseconds won't let it come back. Wait 1 second, then 2, then 4 — and add a bit of randomness to the pause.
How often should I send a heartbeat?
Usually every 20–30 seconds — a bit more often than the shortest proxy timeout on the path. No need to go faster: on mobile every ping wakes the radio and drains the battery.
Are a heartbeat and a health check the same thing?
Close, but no. A health check is someone outside asking a service "are you healthy?". A heartbeat is the service itself regularly reporting "I'm alive".
What if the tab goes into the background?
Browsers throttle timers in background tabs, so pings can run late. When the user returns to the tab, check the connection right away instead of waiting for the next tick.
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





