Server-Sent Events vs WebSockets: Which to Use
SSE streams one-way updates over plain HTTP; WebSockets open a full-duplex channel. How they differ and which fits your real-time feature.
Server-Sent Events (SSE) and WebSockets both push real-time updates from a server to a browser without the client polling for them, but they solve different shapes of problem. SSE is a one-way stream built on plain HTTP; WebSockets are a full-duplex, bidirectional connection over their own protocol. Picking between them comes down to a single question: does the client need to send data back over the same connection, or just receive it?
How each one works
Server-Sent Events are just a long-lived HTTP response. The client opens a connection with EventSource, the server keeps it open, and writes text-formatted events to it whenever it has new data. Because it rides on ordinary HTTP, it inherits things HTTP already does well: automatic reconnection with a Last-Event-ID header so a dropped connection can resume where it left off, standard proxy and load-balancer compatibility, and no separate protocol handshake beyond the normal HTTP request.
WebSockets start as an HTTP request too, but immediately upgrade to a different protocol (ws:// or wss://) via the Upgrade header. Once upgraded, either side can send messages at any time — the server pushes data, and the client can push data right back over the same socket, with no request/response cycle needed for either direction.
The comparison
| Server-Sent Events | WebSockets | |
|---|---|---|
| Direction | One-way: server to client | Full-duplex: both directions |
| Protocol | Plain HTTP | Own protocol (ws/wss), after an HTTP upgrade |
| Message format | Text (UTF-8) only | Text or binary |
| Reconnection | Automatic, built into EventSource | Manual — you write the retry logic |
| Proxy/firewall friendliness | High — looks like a normal HTTP response | Lower — some older proxies mishandle the upgrade |
| Browser API complexity | Minimal (EventSource) | Slightly more (WebSocket, manual message framing) |
| Typical use case | Live feeds, notifications, progress updates | Chat, multiplayer, collaborative editing |
When SSE is the better fit
If the data only flows one direction — a live score ticker, a build-progress log streaming to a dashboard, a notification feed, or streaming an LLM’s response token by token — SSE is usually the simpler, more robust choice. It needs no special server framework support beyond writing a specific content type, it survives flaky networks better because reconnection is automatic, and it plays nicely with existing HTTP infrastructure like load balancers and CDNs that already know how to handle streaming responses. Any request the client needs to make (start a job, change a filter) just goes over a normal HTTP request alongside the SSE stream.
When WebSockets are the better fit
If the client needs to send frequent, low-latency messages back — a chat app, a multiplayer game, a collaborative document editor, or a trading interface where the client submits orders and receives fills over the same channel — WebSockets avoid the overhead of opening a new HTTP request for every client-to-server message. The full-duplex channel also supports binary frames directly, which matters for anything moving non-text payloads efficiently.
Authentication and reconnection in practice
SSE authenticates the same way any HTTP request does — cookies or an Authorization header travel with the initial request, and the browser’s native EventSource API handles reconnection for you, replaying the last event ID so the server can resume the stream where it left off rather than replaying everything from the start. WebSockets need this handled manually: the initial handshake can carry a cookie or token, but if the connection drops, the application code is responsible for detecting it, reopening a new connection, and — if messages need to be replayed — re-synchronizing state, since there’s no built-in equivalent of Last-Event-ID. That extra responsibility is a fair trade for full-duplex communication, but it’s worth budgeting for when choosing WebSockets purely out of habit rather than necessity.
A note on HTTP/2 and multiplexing
One reason SSE fell out of favor briefly, and is now favored again, involves the underlying HTTP version. HTTP/1.1 connections are capped at roughly six per origin in most browsers, so multiple SSE streams to the same origin could exhaust that limit alongside ordinary page requests. HTTP/2 (and HTTP/3) fixed this by multiplexing many streams over a single connection, removing that constraint — one more reason SSE is a practical default today rather than a legacy fallback.
Scaling either one on the server
Both approaches require the server to hold a connection open per connected client, which changes how a backend needs to scale compared to stateless request/response APIs. A server handling thousands of open SSE streams or WebSocket connections needs an architecture built for long-lived connections — typically an event-driven or async I/O model rather than one thread per request — and load balancers in front of either need to be configured for sticky sessions or connection-aware routing, since a reconnect landing on a different backend instance than the one holding relevant in-memory state can behave unexpectedly. This operational cost applies to both technologies roughly equally; it’s not a factor that favors one over the other, but it’s easy to underestimate when a feature that worked fine in local testing suddenly needs to hold ten thousand simultaneous connections in production.
Neither replaces polling everywhere
Both are overkill for data that changes rarely — a dashboard that refreshes every few minutes is usually better served by a plain periodic fetch, which is simpler to reason about and doesn’t hold a connection open unnecessarily. Reach for SSE or WebSockets when updates are frequent and latency actually matters to the user experience.
The takeaway
Choose Server-Sent Events when the server needs to push updates and the client doesn’t need to talk back over the same channel — it’s simpler, reconnects automatically, and rides on ordinary HTTP. Choose WebSockets when both sides need to exchange messages continuously and in real time. Neither is a universal upgrade over the other; match the tool to whether your data actually needs to flow both ways.
Keep reading
Takina · · 4 min read What Is WebRTC? Real-Time Audio, Video, and Data
WebRTC lets browsers stream audio, video, and data directly between peers — no plugins. How getUserMedia, RTCPeerConnection, and ICE/STUN/TURN fit together.
Takina · · 4 min read What Is a WebSocket? Real-Time, Two-Way Web Communication
A WebSocket is a protocol for full-duplex, persistent communication over a single TCP connection. Learn how it works, when to use it, and what the alternatives are.
Takina · · 4 min read TypeScript Abstract Classes, Explained
Abstract classes in TypeScript define shared implementation plus methods subclasses must fill in. How they differ from interfaces and when to reach for them.