Back to the map

Module 05 · Communication patterns · Lesson 2 of 10

Server-sent events

A one-way stream of events over a plain HTTP response. The wire format, automatic reconnection with Last-Event-ID, where SSE beats WebSockets for server-to-client updates, and the proxy and connection-limit pitfalls.

CoreShould-knowMid 10 minVideo Updated Nov 2026
Video walkthrough · 2:57 · narrated, with captions

Why it matters

Most "real-time" features are one-directional: the server has news, the client displays it. Notifications, live dashboards, progress bars, streamed AI responses. WebSockets handle this but bring a second protocol with its own infrastructure needs. Server-sent events do the one-way case with nothing but HTTP, and most teams that reach for WebSockets first would have been better served by them.

The idea

Server-sent events (SSE) is an HTTP response that never ends. The server replies with Content-Type: text/event-stream and writes events as they happen; the browser's built-in EventSource API parses them and fires callbacks. The connection stays open as long as both sides want it.

The wire format is plain text, one event per blank-line-separated block:

id: 1042
event: order.updated
data: {"id": 9001, "status": "shipped"}

: heartbeat comment, ignored by clients

id: 1043
data: plain text works too
BrowserEventSourceServertext/event-streamGET /eventsid:1 · id:2 · id:3 ...connection dropsGET /events Last-Event-ID: 3id:4 · id:5 ...
One long-lived HTTP response carries a sequence of events. On disconnect the browser reconnects automatically and sends the last id it saw.

Built-in reconnection

When the connection drops, EventSource waits (a server-settable retry: interval) and reconnects on its own, sending a Last-Event-ID header with the last id it received. A server that keeps a short buffer of recent events per stream can replay what the client missed, so a brief network blip loses nothing and the application code never sees it. This is the feature WebSocket users end up hand-building.

SSE versus WebSockets

 Server-sent eventsWebSockets
DirectionServer to client onlyBoth directions
ProtocolPlain HTTP; works through any HTTP proxy, CDN and load balancer that supports streamingUpgrade to a separate framed protocol; needs explicit support on every hop
Reconnect and resumeBuilt in, with Last-Event-IDBuild it yourself
PayloadUTF-8 text (JSON works fine)Text or binary frames
Browser connection limit6 per origin on HTTP/1.1, effectively unlimited on HTTP/2Separate limit, usually hundreds
AuthenticationCookies or a query token; no custom headers from EventSourceCookies or a token in the first message or query
FitsFeeds, notifications, progress, streamed textChat, games, collaborative editing, anything the client also sends frequently

The client can still send things to the server: it uses ordinary HTTP requests for that. The question is whether client-to-server messages are frequent and latency-sensitive enough to need a persistent channel of their own. If not, SSE plus regular POSTs is simpler and more robust.

Pitfalls

Buffering proxies and some application servers hold the response until it is "complete", which never happens; disable response buffering (nginx X-Accel-Buffering: no) and compression on the stream route. Send a comment line every 15 to 30 seconds as a heartbeat so idle timeouts do not close the connection and clients can detect a dead one. On HTTP/1.1, browsers allow six connections per origin, so several tabs each holding a stream exhaust the budget; HTTP/2 multiplexes them over one connection and removes the problem. Every open stream is a held connection on the server, so use an event-driven runtime and plan fan-out: a pub/sub layer delivers an event once to each server, which writes it to its local streams.

Trade-offs

SSE is simple, standards-based, proxy-friendly and self-healing, at the cost of being one-way and text-only, and of needing a persistent connection per client just like WebSockets. For server-to-client updates it is almost always the lighter choice; the moment the client needs a low-latency channel upstream, WebSockets earn their complexity.

In an interview

Choose it deliberately: "Order status updates are server-to-client only, so SSE: one GET /events stream per session, ids on every event, a short replay buffer so Last-Event-ID resumes after a blip, heartbeats every 20 seconds, and a Redis pub/sub channel fanning events out to whichever server holds the client's stream. Client actions are ordinary POSTs." Then pre-empt the question: "WebSockets only if we add a feature where the client sends frequently, like chat."

Check yourself

A dashboard needs live metrics pushed to the browser every second, and the browser only ever sends an occasional filter change. Which transport fits best and why?

Server-sent events · Backplane