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.
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
- 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.
- 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.