5 min read

Q06 — What is Server-Sent Events (SSE) and how does it differ from WebSockets?


What problem does SSE solve that WebSockets already solves?

WebSockets give you a full-duplex channel — both the client and server can send messages freely. That's powerful, but it's more than many use cases actually need.

Think about these scenarios: a live sports scoreboard, a stock price feed, a deployment progress log, a notification bell, a dashboard that refreshes with new metrics every few seconds. In all of these, data flows in one direction only — from server to client. The client doesn't need to send anything back over the same channel. It might send a "subscribe to this team" request at the start, but after that, it just listens.

WebSockets would work. But WebSockets requires:

  • A protocol upgrade (the HTTP Upgrade handshake)
  • A persistent stateful connection the server must manage
  • Custom server-side handling — most HTTP frameworks don't treat WebSocket connections like regular HTTP

SSE provides server-to-client push using plain HTTP, without any of this complexity. That's its reason for existing.


How does SSE actually work?

SSE is elegantly simple. The client makes a normal HTTP GET request, and the server responds — but instead of closing the connection after sending the response body, it keeps the connection open and keeps writing data.

The response has a specific content type: text/event-stream. The body is a stream of events in a simple text format:

code
data: {"score": "2-1", "team": "Arsenal"}\n\n
data: {"score": "2-2", "team": "Chelsea"}\n\n
data: {"score": "3-2", "team": "Arsenal"}\n\n

Each event ends with two newlines. The client's browser parses this stream and fires JavaScript events for each message. The connection stays open indefinitely, with the server writing new events whenever there's something to say.

That's it. No new protocol. No handshake. No special server support beyond the ability to keep a response stream open — which every HTTP server can do.


Why does SSE being plain HTTP matter?

Because HTTP infrastructure already works. Proxies, load balancers, CDNs, firewalls — they all understand HTTP GET requests and HTTP responses. An SSE connection is just a long-lived HTTP response. It passes through every proxy that HTTP passes through, requires no firewall rules, and works on port 80 and 443.

WebSocket connections, by contrast, use the ws:// or wss:// protocol after the upgrade. Many enterprise proxies and older load balancers don't understand WebSocket connections and either drop them or can't properly route them. SSE sidesteps this entirely.

TLS/HTTPS works identically with SSE — it's just HTTPS with a long response body. WebSockets over TLS requires wss:// support, which some infrastructure still handles differently.


What does SSE give you that long polling doesn't?

SSE and long polling both use HTTP and both enable server push. The difference is the connection lifecycle.

In long polling, every event causes the connection to close. The client immediately reconnects. You pay a TCP handshake and HTTP overhead for every event.

In SSE, the connection stays open. The server writes events one after another on the same connection. You pay the connection cost once, no matter how many events flow. On a high-frequency feed (say, 10 price updates per second), long polling would thrash with 10 reconnects per second while SSE maintains one steady connection.

SSE also has built-in reconnect logic in the browser. If the connection drops, the browser automatically reconnects — and sends a Last-Event-ID header with the ID of the last event it received, so the server can resume from where it left off. Long polling had to implement this manually.


How is SSE different from WebSockets?

The difference is directionality and complexity.

Directionality: SSE is one-way — server to client only. WebSockets are full-duplex. If you need the client to send messages back (chat, collaborative editing, gaming), SSE isn't enough on its own — you'd pair it with regular HTTP POST requests for client-to-server communication, or just use WebSockets.

Complexity: SSE is dramatically simpler to implement. On the server side, you set a content type, keep the response body open, and write text. No protocol upgrade, no WebSocket library required. On the client side, new EventSource('/events') is all you need in JavaScript.

Protocol: SSE is HTTP. WebSocket is its own protocol that starts with HTTP and then upgrades. This means SSE works through HTTP/2 for free — a single HTTP/2 connection can carry SSE alongside regular requests, benefiting from HTTP/2's multiplexing. WebSockets don't get HTTP/2's multiplexing.

Browser reconnection: SSE has automatic reconnect with event ID resumption built into the browser spec. WebSockets require the application to implement reconnect logic.

Binary data: SSE is text-only. If you need to stream binary data (audio, video frames, raw bytes), SSE requires base64 encoding which adds overhead. WebSockets support binary frames natively.


When should you use SSE vs WebSockets?

Use SSE when: the server pushes, the client listens, and simplicity matters. Live feeds, notifications, progress bars, log streaming, dashboard updates. SSE is the right tool whenever the communication is genuinely one-directional.

Use WebSockets when: the client also sends messages frequently, or you need low-latency bidirectional communication. Chat, collaborative document editing, multiplayer games, trading interfaces where orders are placed. If you find yourself needing the client to send more than occasional HTTP POSTs, you want WebSockets.

A common pattern: use SSE for read-heavy real-time UIs (dashboards, feeds) where the client occasionally sends actions via regular HTTP. Use WebSockets for interactive, high-frequency bidirectional communication.


The mental model

code
Long polling:  fake push via repeated HTTP requests → reconnect overhead per event
SSE:           real push via persistent HTTP response → one connection, many events, server-to-client only
WebSockets:    full-duplex persistent channel → either side sends anytime, binary support

SSE vs WebSockets:
  ├── SSE: plain HTTP, one-direction, automatic reconnect, works through all proxies
  └── WebSockets: upgraded protocol, bidirectional, more infrastructure support needed

Choose SSE when: server pushes, client listens, simplicity is a virtue
Choose WebSockets when: client also sends frequently, binary data, high-frequency bidirectional

SSE exists because full-duplex is overkill for many real-time features, and using a simpler tool means less complexity, better infrastructure compatibility, and code that's easier to reason about.