Skip to content

Connection

Heartbeat

View as MarkdownPlain text source(opens in new tab)

How to keep an idle WebSocket connection open. Protocol pings and the text fallback for clients that can't send them.

If your connection has no traffic for about 100 seconds, the network will drop it. There are two ways to keep an idle connection open. Pick whichever fits your client library.

Most server-side WebSocket libraries send protocol-level ping frames on a timer. The network edge replies automatically with a pong; nothing reaches your application code. This is the standard mechanism and the cheapest to run.

How to enable per library

LibraryHow
Python websocketsAutomatic every 20 seconds. Configurable with ping_interval
Node ws packageCall ws.ping() from a setInterval(..., 30_000)
Go github.com/coder/websocketCall ws.Ping(ctx) from a time.Ticker loop
Rust tokio-tungsteniteSend Message::Ping(vec![]) from a tokio::time::interval
Ruby async-websocketSend a ping from an Async task on a timer

A 30-second interval is a safe default. Anything below 100 seconds works.

Option 2: Text fallback

Some clients cannot send protocol pings at all. Two common cases:

  • Browser WebSocket is built on the WHATWG spec, which does not expose pings to JavaScript
  • Node’s built-in WebSocket (available in Node 22+) follows the same WHATWG spec

For these clients, send a text frame containing the literal string "ping" every 30 seconds. The server replies with the text string "pong".

Example (browser or Node built-in)

setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send("ping");
  }
}, 30_000);

The "pong" reply arrives as a regular text message. Your message handler will receive "pong" as the frame data; treat it as a no-op or skip JSON parsing for that frame.

Detecting a dead connection

Protocol pongs and text "pong" replies are quiet on the application side, so a dead connection is detectable only by absence:

  • If you sent a ping and don’t see traffic (either a pong or a real event) within ~60 seconds, assume the connection is dead and reconnect.
  • If you receive any event, the connection is alive; you don’t need to track pong arrivals separately.

See Reconnection for what to do once you’ve decided to reopen.