Card 08/ 10

GotchaDifficulty: Advanced2 min

Idle Timeouts That Close Quiet Connections

A WebSocket with nothing to say for a minute is cut by a load balancer that thinks it is dead.

Load balancers and proxies close connections that carry no data for a set time, their idle timeout, and a real-time connection with nothing to say looks exactly like an abandoned one. The fix is a heartbeat: a small message sent more often than the shortest timeout on the path, so the connection is never idle long enough to be cut.

Picture a dashboard whose live updates stop every 60 seconds, exactly, whenever the market is quiet. The browser reconnects, the dashboard works again, and a minute of silence later it stops again.

The symptom. Connections that close at a fixed interval after the last message, never during busy periods, with no error on the server.

A quiet WebSocket through a load balancer with a 60-second idle timeout
  1. 00:00

    The last update is sent

    The market goes quiet; no frames pass in either direction.

  2. 01:00

    The load balancer closes the connection

    Sixty seconds without data, so it ends both sides.

  3. 01:02

    The application reconnects

    It opens a new WebSocket and waits for the next update.

The same connection with a ping every 30 seconds
  1. 00:00

    The last update is sent

  2. 00:30

    The server sends a ping frame

    The browser answers with a pong, and the idle clock restarts.

  3. 01:00

    Another ping and pong

    The connection is never quiet for 60 seconds, so it stays open.

Why it happens. A load balancer holds memory for every open connection, so it frees those that seem dead. AWS Application Load Balancers default to 60 seconds, and nginx closes a proxied connection after 60 seconds without a read. TCP's own keepalive does not help, because its probes start after two hours by default and many proxies do not count them.

What it costs. Missed updates in the gap before reconnecting, a burst of reconnections whenever activity drops, and a bug that never appears in local testing, where no load balancer stands in between.

What to do. Send a heartbeat shorter than the shortest timeout on the path, typically every 20 to 30 seconds. A server can send WebSocket ping frames, which browsers answer automatically; browser code cannot send pings itself, so a client-side heartbeat is an ordinary small message. For SSE, the server sends a comment line. Raising the timeout helps too, but only on infrastructure you control, so heartbeats are the fix that holds.