How long an SSE connection stays open
Your server-sent events stream disconnects on its own and you need to know which layer is closing it.
Almost always a proxy idle timeout, and sixty seconds is the most common value. Nginx defaults to sixty seconds of proxy read timeout, many cloud load balancers default to sixty, and browsers themselves impose no limit. A stream sending nothing for longer than the shortest timeout in the chain is closed by whichever hop is strictest.
The chain decides, not the server
Between the browser and the application there may be a CDN, a load balancer, a reverse proxy and a process manager, each with an idle timeout. The shortest one wins, and it is rarely the one you configured. The symptom is a disconnect at a suspiciously round interval.
Why a heartbeat fixes it
Idle timeouts measure time since the last byte, not since connection start. A comment line every twenty to thirty seconds resets every timer in the chain and costs almost nothing. This is why SSE implementations send colon-prefixed keepalives.
Reconnection is not free but it is cheap
The browser reconnects automatically and sends Last-Event-ID so the server can resume. That built-in behaviour is a large part of what SSE offers over a raw socket — but it only helps if the server actually honours the header rather than restarting the stream.
Detecting a stall that is not a disconnect
A connection can stay open while delivering nothing, which is worse than a clean disconnect because the browser has no reason to reconnect. Sending a keepalive on a fixed cadence lets the client apply its own timeout: if nothing arrives for more than twice the interval, it can close and reopen rather than waiting on a socket that will never speak again.
Common idle timeouts in the chain
Common idle timeouts in the chain
| Layer | Typical default |
| Browser | no limit |
| Nginx proxy_read_timeout | 60 seconds |
| Cloud load balancer | 60 seconds |
| PHP-FPM / app worker | 30-60 seconds |
| Corporate proxy | varies, often shorter |
Key facts
- A sixty-second idle timeout is the most common default in the chain, and the shortest timeout among all hops is the one that closes the stream.
- Idle timeouts measure time since the last byte sent, so a keepalive comment every twenty to thirty seconds keeps the connection open indefinitely.
- Browsers impose no time limit on an EventSource connection; a disconnect at a round interval points at infrastructure rather than the client.
- The browser reconnects automatically and sends Last-Event-ID, but resumption only works if the server honours that header instead of restarting the stream.
- Output buffering anywhere in the chain defeats SSE entirely, because events are held until the buffer flushes rather than arriving as they happen.
Frequently asked questions
My stream dies at exactly 60 seconds. Where do I look?
A round number is infrastructure. Check the reverse proxy read timeout first, then the load balancer idle timeout. Sending a keepalive is usually a smaller change than raising both.
Do I need to raise the proxy timeout if I send keepalives?
No, and that is the appeal. A keepalive resets every timer in the chain without needing access to any of them, which matters when some hops are not yours to configure.
How does the client tell a quiet stream from a dead one?
It cannot, unless the server sends something on a known cadence. That is the second reason for keepalives: they let a client set a timeout of its own instead of trusting an open socket to mean a working one.
Machine-readable copy of this page:
/guide/how-long-an-sse-connection-stays-open.md