WebSockets vs SSE vs Long Polling

HTTP was designed for the client to ask and the server to answer. Real-time features — notifications, live dashboards, chat, collaborative editing, streaming AI responses — need the server to send data when something happens. Three techniques dominate: long polling, which bends ordinary HTTP requests into a push channel; Server-Sent Events (SSE), a standard one-way stream from server to browser over HTTP; and WebSockets, a full-duplex connection that both sides can write to at any time.

Short verdict: use SSE when the server pushes and the client mostly listens — notifications, feeds, progress, token-by-token LLM output. Use WebSockets when both sides send frequent messages with low latency — chat with typing indicators, multiplayer games, collaborative editors. Use long polling only as a fallback where streaming connections are blocked or for very infrequent updates. Most applications that reach for WebSockets would be simpler with SSE plus ordinary HTTP requests for the client's actions.

TL;DR

Quick Example

The same "new notification" feature with SSE — a Node.js endpoint and the browser's built-in EventSource:

And the WebSocket equivalent, where the client can also send messages on the same connection:

Core Concepts

Head-to-Head

Long Polling

The client asks for updates; the server holds the request open — often 20–30 seconds — and responds as soon as data arrives or the timeout expires, and the client immediately asks again. It works through any proxy and needs no special server support, but each message costs a full request, messages arriving between polls must be buffered, and ordering and gaps need a cursor such as "events after ID 4182".

Server-Sent Events

SSE is one HTTP response that never finishes. The server writes events as lines of text; the browser's EventSource parses them, dispatches named events, and reconnects automatically, sending the last received event ID so the server can replay what was missed. Because it is ordinary HTTP, it works with existing authentication cookies, compression, and HTTP/2 multiplexing. See Server-Sent Events.

WebSockets

A WebSocket starts as an HTTP request with an Upgrade header and becomes a persistent, full-duplex connection carrying framed messages in both directions. It is the right tool when the client sends frequent small messages and latency matters. In exchange, you implement reconnection, heartbeats, message acknowledgment, and resumption yourself — or use a library or managed service that does. See WebSockets.

Infrastructure and Scaling

Connections and Browser Limits

Over HTTP/1.1, browsers allow only about six concurrent connections per origin, and every open SSE stream uses one — users with many tabs can exhaust them. HTTP/2 and HTTP/3 multiplex streams over one connection, which removes the problem for SSE. WebSockets open their own connections regardless.

Load Balancers and Proxies

Fan-Out Across Servers

With more than one server, the server holding a user's connection may not be the one that produced the event. A pub/sub layer — Redis pub/sub or streams, a message broker, or a managed real-time service — distributes events to whichever server holds each connection. This is the same for all three techniques and is usually the hardest part of real-time architecture.

The Decision Framework

A useful rule: if the client's messages could be ordinary POST requests without anyone noticing the latency, use SSE for the downstream channel and HTTP for the upstream.

Best Practices

Design for Reconnection and Missed Messages

Every connection drops eventually — deployments, mobile networks, laptop sleep. Give events IDs, let clients resume from the last one they saw, and fall back to a full refresh when the gap is too large.

Send Heartbeats

Comments in SSE (: ping) or ping messages over WebSockets keep idle connections alive through proxies and detect dead connections quickly.

Authenticate the Connection and Authorize Each Channel

Check credentials when the connection opens and check permissions when a client subscribes to each channel or topic. Long-lived connections also need a plan for expired sessions and revoked access.

Bound Per-Connection Memory

Slow clients cause server-side buffers to grow. Cap queues per connection and disconnect clients that cannot keep up, rather than letting one slow consumer exhaust memory.

Common Mistakes

WebSockets for One-Way Updates

Proxy Buffering an Event Stream

Forgetting Multi-Server Fan-Out

FAQ

Is SSE better than WebSockets?

For server-to-client updates, often yes: it is simpler, reconnects automatically, works over plain HTTP with existing authentication, and multiplexes over HTTP/2. WebSockets are better when the client sends frequent messages and needs the lowest possible latency in both directions.

Is long polling still used?

Yes, as a fallback where streaming connections are blocked, and for infrequent updates where simplicity matters more than efficiency. Several real-time libraries use it as a transport of last resort.

Can SSE send binary data?

Not directly — events are UTF-8 text. Binary data can be base64-encoded, or fetched separately via a URL included in the event. WebSockets support binary frames natively.

Why do my SSE events arrive late or in bursts?

Usually because a proxy, load balancer, or compression middleware is buffering the response. Disable buffering for the stream endpoint and flush after each event.

What is WebTransport?

A newer browser API built on HTTP/3 that offers multiple streams and unreliable datagrams over one connection, useful for games and media. Support and tooling are still maturing, so WebSockets and SSE remain the common defaults.

Related Topics

References