The Seductive Simplicity of SSE
On paper, Server-Sent Events are a developer’s delight. Need to push notifications, live scores, or stock price updates to a user? SSE lets you do it over a standard HTTP connection without the overhead
of its more famous cousin, WebSockets. The client-side code is trivial: you create an `EventSource` object pointing to a server endpoint, and listen for messages. That’s it. No complex setup, no special protocol, just plain old HTTP. On the server side, it’s just as straightforward. You set a few headers (`Content-Type: text/event-stream`, `Cache-Control: no-cache`) and then write data to the response stream in a simple `data: message ` format. Compared to the bidirectional, protocol-switching dance of WebSockets, SSE feels like a breath of fresh air. It’s designed for a specific job—server-to-client streaming—and the API reflects that elegant focus. This simplicity is its greatest marketing tool, promising real-time functionality without a steep learning curve or adding a new library. It’s the kind of technology that makes you think, “Why isn't everything this easy?”
The Reconnection 'Feature' That Bites Back
One of the most touted features of SSE is automatic reconnection. If the connection drops, the browser will automatically try to reconnect after a few seconds, even remembering the ID of the last message it received so the server can send any missed updates. This sounds fantastic—a self-healing connection with no extra code required. But here’s the catch: the browser doesn’t know *why* the connection dropped. Was it a temporary network hiccup, or did you take the server down for maintenance? The browser doesn't care; it will keep trying to reconnect. This can lead to a “thundering herd” problem, where thousands of clients simultaneously hammer your server as it’s trying to restart, potentially bringing it down again. While you can configure the retry delay, the default behavior can be surprisingly aggressive. What looks like a convenience feature can quickly become a self-inflicted denial-of-service attack if not managed carefully in your infrastructure.
The Invisible Wall of Proxies and Firewalls
SSE works by keeping an HTTP connection open for a long time. In a clean, developer-to-local-server environment, this is fine. In the real world, your traffic passes through countless intermediaries: corporate firewalls, NGINX reverse proxies, cloud load balancers, and more. Many of these are configured to kill idle connections or buffer responses, both of which are fatal to SSE. A proxy might see an open connection with no data flowing for 30 seconds and assume it's stale, terminating it. Another might try to buffer the entire response before sending it to the client, which defeats the purpose of real-time streaming. You won't get an error; your events will just stop arriving. Debugging this is a nightmare, as the problem exists somewhere in the nebulous infrastructure between your server and your user. Getting SSE to work reliably often involves painstakingly configuring every layer of your network stack to play nice with long-lived, unbuffered connections.
The Hidden Cost of State and Connection Limits
Unlike a stateless REST API where a server handles a request and then forgets about it, every active SSE client holds an open connection. This means your server has to maintain state for every single connected user. A thousand users means a thousand open TCP connections. This has significant implications for server memory and resource management. It’s a fundamentally different scaling challenge than stateless traffic. Furthermore, browsers impose their own limits. A browser might only allow a handful of open connections per domain (historically, as few as six). If your user has your app open in multiple tabs, they can quickly exhaust this limit, and new SSE connections (or even requests for images and APIs) will fail to open. While HTTP/2 mitigates this by multiplexing requests over a single connection, many environments still fall back to HTTP/1.1, where this hard limit is a very real constraint that can lead to puzzling and inconsistent bugs.






