← Recipes

阅读中文版本

Diagnose SSE Heartbeat Timeout Disconnects

Measure server-sent event heartbeat intervals against proxy idle timeouts to stop long-lived streams from being cut mid-flight.

~25 minssestreamingdebugging

Task

Verify SSE heartbeat cadence keeps the stream alive through proxies and clients with idle timeouts.

When to use this

A live dashboard stream disconnects after about a minute even though events should flow continuously during quiet periods.

Prerequisites

  • An SSE endpoint in staging
  • curl to observe the raw stream
  • Knowledge of proxy idle timeouts

Steps

1. Observe heartbeat timing on the raw stream.

curl -N -s https://api.example.com/events | \
  awk '{ print strftime("%H:%M:%S"), $0; fflush() }'

Expected output: Heartbeat comment lines appear at a fixed cadence; gaps longer than the proxy timeout explain the disconnect.

2. Emit a comment heartbeat below the shortest idle timeout.

const heartbeat = setInterval(() => {
  res.write(': keep-alive\n\n');
}, 15000);
req.on('close', () => clearInterval(heartbeat));

Expected output: A comment is sent every 15 seconds so no proxy sees the connection as idle.

Troubleshooting

Problem

Browser reconnects repeatedly

Solution

Set retry and Last-Event-ID handling so missed events are replayed on reconnect.

Problem

Heartbeats stop under load

Solution

Move heartbeats to a separate timer not tied to event processing so a slow handler cannot starve them.

Limitations

  • Heartbeats add traffic proportional to connection count.
  • Does not persist events for reconnects.
  • HTTP/2 multiplexing may change timeout behaviour.

What this recipe does

Verify SSE heartbeat cadence keeps the stream alive through proxies and clients with idle timeouts.

A live dashboard stream disconnects after about a minute even though events should flow continuously during quiet periods.

Prerequisites

  • An SSE endpoint in staging
  • curl to observe the raw stream
  • Knowledge of proxy idle timeouts

Procedure

  1. Observe heartbeat timing on the raw stream.
curl -N -s https://api.example.com/events | \
  awk '{ print strftime("%H:%M:%S"), $0; fflush() }'

Expected result: Heartbeat comment lines appear at a fixed cadence; gaps longer than the proxy timeout explain the disconnect.

  1. Emit a comment heartbeat below the shortest idle timeout.
const heartbeat = setInterval(() => {
  res.write(': keep-alive\n\n');
}, 15000);
req.on('close', () => clearInterval(heartbeat));

Expected result: A comment is sent every 15 seconds so no proxy sees the connection as idle.

How to confirm it worked

Run the commands above and confirm every expected result in order. If a step does not produce the stated output, use the troubleshooting notes before continuing. A passing run shows the stream stays connected well beyond the previous timeout, with heartbeats arriving at the configured cadence.

Troubleshooting

Browser reconnects repeatedly — Set retry and Last-Event-ID handling so missed events are replayed on reconnect.

Heartbeats stop under load — Move heartbeats to a separate timer not tied to event processing so a slow handler cannot starve them.

Limitations

  • Heartbeats add traffic proportional to connection count.
  • Does not persist events for reconnects.
  • HTTP/2 multiplexing may change timeout behaviour.