WebSockets vs SSE vs long polling
Published
HTTP was designed for the browser to ask and the server to answer. But a lot of products need the server to speak first: a new WhatsApp Web message, a wicket in the IPL final, a Swiggy order that just left the restaurant, a stock price tick. There are four ways to do it, and they trade simplicity against cost and against which direction data needs to flow.
During an India vs Pakistan match, 50 million people have a live score page open. The score changes about once every 30 seconds, and nobody sends anything back. Would you hold 50 million WebSocket connections? What else could work?
The four options#
Short polling is a timer. It is trivially simple, works through every proxy and caches well, but most requests return nothing and updates arrive late by up to one interval.
Long polling is a polite trick on plain HTTP: the server holds the request until it has something to say. Near real time, works everywhere, but every message costs a new request, and you must handle the gap between one response and the next request.
Server sent events (SSE) keep one HTTP response open and stream text events down it. The
browser's EventSource handles parsing and reconnects automatically, resuming from the
last event id. It only goes from server to client, which is all a score feed, a notification
bell or a token by token AI answer needs. (ChatGPT style streaming responses use exactly this
format.)
WebSockets upgrade an HTTP connection into a full duplex channel. Either side sends messages at any time with tiny overhead. They are the right tool when the client talks a lot too: chat, multiplayer games, collaborative editors, trading screens. You get no automatic reconnection, no resume and no HTTP caching or semantics; you build those yourself or use a library like Socket.IO.
const events = new EventSource('/matches/ind-pak/stream')
events.onmessage = (e) => render(JSON.parse(e.data))
// Dropped connection? EventSource reconnects by itself and sends Last-Event-ID.What it costs at scale#
Persistent connections are cheap per message and expensive to hold. Each open connection costs memory on the server (kernel buffers plus your application's per connection state), a file descriptor, and a slot on the load balancer. Polling holds nothing but repeats work.
Both are big numbers. The difference: a CDN can absorb millions of identical cached GETs a second at the edge, while every open connection needs your own servers. For a read only score that changes every 30 seconds, cached polling is the boring answer that works. Push only the users who need sub second updates.
Persistent connections also change how you scale and deploy. Connections are sticky: a user's messages arrive on whichever server holds their connection, so servers need a pub/sub backplane (Redis pub/sub, NATS, Kafka) to deliver a message to a user connected elsewhere. A deploy has to drain connections gently or a million clients reconnect at the same moment and hammer your auth service, the thundering herd. Add jitter to reconnect delays.
| Choice | What you gain | What you pay | Pick it when |
|---|---|---|---|
| Short polling | Simplest, stateless, cacheable at a CDN, works through anything. | Wasted requests, latency up to one interval. | Updates every few seconds or slower, huge read only audiences, dashboards. |
| Long polling | Near real time over plain HTTP, works with old proxies. | A request per message, gaps between requests, holds server resources. | A fallback when SSE and WebSockets are blocked. Rarely the first choice now. |
| Server sent events | Plain HTTP, auto reconnect with resume, works with HTTP/2 multiplexing and normal auth. | Server to client only, text only. Six connection limit per domain on HTTP/1.1. | Feeds, notifications, live scores, progress, streaming LLM output. |
| WebSocket | Full duplex, low overhead per message, binary support. | Stateful, sticky, no built in reconnect or resume, some proxies interfere. | Chat, games, collaborative editing, anything chatty in both directions. |
- Socket.IOWebSocket library with fallbacks and rooms
- RedisPub/sub backplane between connection servers
- CloudflareDurable Objects, edge WebSockets
- AWSAPI Gateway WebSocket APIs, AppSync subscriptions
In the interview#
Checkpoint#
1. Which option reconnects and resumes from the last event automatically?
2. Why might short polling beat WebSockets for a huge read only audience?
3. A user connected to server A must receive a message produced on server B. What is needed?
- Short poll: timer. Long poll: held request. SSE: one way stream. WebSocket: two way channel.
- SSE auto reconnects and resumes with Last-Event-ID. WebSockets do not.
- Persistent connections need a pub/sub backplane and careful deploys.
- Huge read only audiences: CDN cached polling is often the right answer.
- The socket is a delivery shortcut. Storage is the source of truth.
There are four ways to push updates to a browser. Short polling asks on a timer; it is simple and cacheable but wasteful and late by up to one interval. Long polling holds each request until there is news, giving near real time over plain HTTP at the cost of a request per message. Server sent events keep one HTTP response open and stream events from server to client, with automatic reconnect and resume via Last-Event-ID, which suits feeds, notifications, scores and streaming LLM output. WebSockets upgrade to a full duplex channel with tiny per message overhead, right for chat, games and collaborative editing, but you build reconnection yourself. Persistent connections cost memory per connection, need a pub/sub backplane to reach users on other servers, and need careful draining on deploy. For a huge read only audience like a live score, I would poll a CDN cached endpoint, and keep messages in storage so the connection is never the source of truth.