Real-time delivery — polling, long-polling, WebSockets, SSE
Four ways to get fresh data from server to client, from crude polling to full-duplex sockets — and how to pick for chat, feeds, and dashboards.
The core problem
HTTP is request–response: the client asks, the server answers. But many features need the server to push fresh data — a new chat message, a live score, a notification. These four techniques bridge that gap, trading simplicity for immediacy and efficiency.
Short polling
The client asks “anything new?” on a fixed timer (every few seconds). Simple and works everywhere, but wasteful: most requests return nothing, and updates lag by up to one interval. Fine for low-frequency, non-urgent data; bad at scale.
Long polling
The client makes a request and the server holds it open until it has data (or a timeout), then responds; the client immediately re-requests. Updates arrive near-instantly with far fewer empty responses than short polling. Costs: many held-open connections, and it’s still one message per round trip (no true server push stream). A solid, broadly-compatible choice.
WebSockets
A single TCP connection, upgraded from HTTP, that stays open for full-duplex (both directions, anytime) messaging. The server can push the instant something happens, and the client can send just as freely — low overhead per message.
- Best for: chat, multiplayer, collaborative editing, live trading — anything bidirectional and high-frequency.
- Costs: stateful long-lived connections (harder to load-balance and scale — you need sticky routing or a pub/sub backplane), and they need fallbacks where sockets are blocked.
Server-Sent Events (SSE)
A long-lived HTTP connection over which the server streams events to the client — but one-directional (server → client only). Lighter than WebSockets, runs over plain HTTP, and auto-reconnects.
- Best for: live feeds, notifications, dashboards, progress updates — push where the client doesn’t need to stream back.
- Costs: server→client only; limited by browsers’ per-domain connection caps (mostly a non-issue over HTTP/2).
Choosing
| Need | Reach for |
|---|---|
| Occasional, non-urgent updates | Short polling |
| Near-real-time, broad compatibility | Long polling |
| Two-way, high-frequency | WebSockets |
| One-way server push, simple | SSE |
The decision rule: does the client need to push too? Yes → WebSockets. No, just receive a stream → SSE. Need it dead simple or maximally compatible → long polling. (These reappear as a sharper trade-off, with webhooks added for server-to-server, in Chapter 3.)
The scaling footnote
All persistent-connection approaches (long poll, WebSockets, SSE) hold state per client on the server, so a million concurrent users means a million open connections. That’s why real-time systems put a pub/sub layer behind the connection servers: connection nodes stay dumb and stateless-ish, subscribing to channels, while a message broker fans events out to them.