Updated 9 hours ago
HTTP has a fundamental rhythm: request, response, done. The client asks, the server answers, the connection closes. This works brilliantly for fetching web pages, but it creates a problem for real-time applications. What if the server has something to say and the client isn't asking?
Server-Sent Events break that rhythm. The server responds... and keeps responding. The connection stays open. When the server has news—a stock price changed, a message arrived, a build finished—it pushes that news down the open pipe. No polling. No asking. The server talks when it has something to say.
The Shape of the Solution
Imagine you're watching election results. The old way: your browser asks "any updates?" every five seconds. The server usually says "no." Bandwidth wasted. Server hammered with requests. And when results do change, you might wait up to five seconds to see them.
SSE inverts this. Your browser opens one connection and says "tell me when something happens." The server holds that line open. When results change, it immediately pushes: "District 7 just reported." You see it instantly. One connection. No wasted requests. Updates arrive the moment they exist.
WebSockets solve this too, but they're bidirectional—built for chat applications where both sides talk constantly. SSE is simpler: it's just HTTP that doesn't hang up. Your existing proxies, load balancers, and caching layers already understand it.
How It Works
The client opens a connection:
That's it. One line to connect, one listener for messages.
The server keeps the connection alive and writes events as they happen:
The text/event-stream content type tells the browser this is SSE. The double newline (\n\n) marks the end of each event. Everything else is just HTTP.
Event Format
SSE events are plain text with a simple structure:
data: carries the payload. Multiple data: lines join with newlines.
event: names custom event types. Without it, events fire as "message."
id: tags events for reconnection. If the connection drops, the browser tells the server "last I heard was 42" and the server can replay what was missed.
retry: suggests reconnection delay in milliseconds.
Comments start with a colon and keep connections alive through aggressive proxies:
Automatic Reconnection
This is where SSE quietly excels.
When a connection drops—network hiccup, server restart, laptop waking from sleep—the browser automatically reconnects. No code required. It just works.
Better: if your events had IDs, the browser sends Last-Event-ID: 42 on reconnection. Your server can replay missed events. Brief disconnections don't lose data.
The default retry is 3 seconds. Servers can adjust this per-connection with the retry: field.
Custom Event Types
Real applications have different kinds of updates:
The server sends typed events:
Each type has its own listener. Clean separation.
Where SSE Shines
Dashboards and monitoring. Server metrics, application health, system status—these flow one direction. SSE pushes updates the instant they're available.
Notifications. New messages, mentions, alerts. The server knows when something happens and tells you immediately.
Live feeds. News, sports scores, social updates. Content appears as it's published.
Progress tracking. File uploads, report generation, batch jobs. Push percentage complete rather than polling for status.
Collaborative viewing. Multiple people watching the same data can all see updates simultaneously. (Full collaboration with editing usually needs WebSockets.)
The Limitations
One direction only. SSE is server-to-client. Clients send data via normal HTTP requests (POST, PUT). This is usually fine—most apps need lots of server updates but only occasional client actions.
Six connections per domain. In HTTP/1.1, browsers limit concurrent connections. One SSE connection to your API domain leaves five for everything else. HTTP/2 fixes this, but it's worth knowing.
Text only. Binary data must be base64 encoded. For streaming video or large files, this adds overhead.
No Internet Explorer. Edge supports it. Chrome, Firefox, Safari support it. IE never did. Polyfills exist but add complexity.
SSE vs. WebSockets
Choose SSE when:
- Updates flow primarily server-to-client
- You want maximum simplicity
- Standard HTTP infrastructure matters (proxies, CDNs, load balancers)
- Automatic reconnection with message replay is valuable
Choose WebSockets when:
- Both sides send frequent messages
- Client-to-server latency matters (gaming, collaborative editing)
- You need raw binary data
- You're already managing WebSocket infrastructure
Many applications use both: SSE for the firehose of server updates, standard HTTP POST for the occasional client action.
Security
SSE is just HTTP, so standard security applies.
Authentication: Cookies or tokens work normally. The EventSource constructor accepts withCredentials for cross-origin cookies.
Authorization: Verify permissions when the connection opens. Don't send events a user shouldn't see.
CORS: Cross-origin SSE needs proper headers. Access-Control-Allow-Origin and friends.
HTTPS: Use it. wss:// for WebSockets, https:// for SSE. Same encryption, same certificates.
Scaling
Every SSE connection is an open HTTP connection consuming server memory. At scale, this matters.
Connection limits: Know how many your servers handle. Plan capacity around peak concurrent users.
Horizontal scaling: If users connect to different servers, events must reach all of them. Redis Pub/Sub or similar message brokers distribute events across your fleet.
Sticky sessions: Once connected, users should stay on the same server (or handle reconnection gracefully).
Heartbeats: Send periodic comments (: ping) to keep connections alive through proxies that kill idle connections.
Debugging
Browser DevTools show SSE in the Network tab. You can watch events arrive in real-time, see connection headers, and spot disconnections.
The EventSource has a readyState property:
0= connecting1= open2= closed
Frequently Asked Questions About Server-Sent Events
Was this page helpful?