Dev Skills

Long polling vs WebSockets vs SSE

Three ways to push data to a browser, with very different costs at scale.

1

What problem does WebSockets solve?

HTTP is built around the client asking. For anything live (a score, a message, a cursor moving) you need the server to speak first, and there is no one obvious way to do that.

The three practical options mostly differ in what they cost you per connected user, and that is the thing that decides the design.

2

What WebSockets costs you

What it costs you
All of them mean holding connections open, so your servers now hold state in a way plain request-response does not. Load balancers have to handle long-lived connections. A deploy drops everyone at once, and they all reconnect together. And capacity is measured in connections at once, not requests per second. Every option needs a plan for reconnecting, and a way to catch up on what was missed while disconnected.

Every concept in here states a cost. A technique with no price is a technique you have not understood yet — and naming the cost out loud is most of what separates a senior answer from a junior one.

3

When to reach for WebSockets, and when not to

Use it when

  • Polling: updates are rare, or a delay does not matter. Often the right answer, and easy to overlook.
  • SSE: server to client only. Notifications, live scores, progress, streaming text. It is plain HTTP and reconnects by itself.
  • WebSockets: both directions and frequent. Chat, collaborative editing, games, live cursors.
  • Long polling: you need push and cannot use the others. Mostly a fallback now.

Do not, when

  • WebSockets for a feed that updates every 30 seconds. You are paying for a permanent connection to send almost nothing.
  • SSE when the client also has to send often. It only goes one way.
  • Any of them when a five-second poll would keep the user happy. Simplicity is worth real money here.
4

How WebSockets actually works

A
Server-Sent Events
  • —One direction: server to client. The client still posts normally over HTTP.
  • —Plain HTTP, so proxies, compression and auth all behave normally.
  • —Reconnects by itself and sends Last-Event-ID, so the server can resume from where the client left off.
  • —Text only. Over HTTP/1.1, browsers allow about six connections per domain, shared across tabs. HTTP/2 lifts that limit.
  • —Right for: notifications, live scores, progress bars, streamed responses.
B
WebSockets
  • —Both directions, and very little overhead per message once connected.
  • —Binary or text, and very low latency.
  • —You build reconnect, resume and heartbeats yourself.
  • —More infrastructure care: sticky routing, proxy config, capacity counted in connections.
  • —Right for: chat, collaborative editing, live cursors, games.
Which one: If the data only flows one way, use SSE. It is much less to run, and reconnection is free. Reach for WebSockets when the client also needs to send often. And check first whether a 5-second poll is fine. It often is, and it needs no new infrastructure.
Pick by direction and by frequency. Both of those change the cost per user.

Long polling works like this. The client makes a request. The server holds it open until there is something to say, or a timeout hits. Then the client immediately asks again. It works everywhere, and every message costs a full HTTP request cycle plus a reconnect.

All three share the same scaling problem. With a million connected users you are holding a million connections, spread over servers that have to find each other. When user A sends a message to user B on a different server, something has to route it. That is usually a pub/sub layer, where each server subscribes to the channels its own users care about. That layer becomes your limit, and it is where the design work in live push goes.

Deploys are the operational trap. Restart the fleet and every client reconnects at once. That is a stampede on connection setup and login, and it can be worse than the traffic you normally serve. The defences are reconnect with growing delays and randomness on the client, and rolling the fleet slowly instead of all at once.

Messages missed while disconnected are a product requirement, not a transport feature. Whatever you choose, the client should reconnect with the last id it saw and get what it missed. That means the server keeps a short buffer per channel. SSE gives you the id mechanism for free. With WebSockets you build it.

A useful hybrid: keep the live connection only for a small message saying "something changed", and have the client fetch the actual data over normal HTTP. The permanent connection carries tiny messages, the real payloads travel over cacheable requests, and you get most of the benefit for much less complexity.

Common trap
  • Choosing WebSockets by default because it sounds more capable.
  • No reconnect-and-catch-up story, so users silently miss messages.
  • Forgetting that deploys disconnect everyone at the same moment.
Say this
"SSE, because updates only flow server to client and reconnection with last-event-id is built in. Connections are grouped by channel so one server holds the subscribers for a topic. Cost: I now have long-lived connections, so deploys need a slow roll and clients need randomised reconnect delays, or every restart is a self-inflicted spike."
5

The WebSockets follow-up an interviewer will ask

"You have a million concurrent connections. What breaks first?"

What a strong answer sounds like

Almost never the raw connection count on the machines. A tuned server handles a lot of idle sockets, and it comes down to memory per connection and file descriptor limits, which is a capacity planning question. What breaks first is usually the fan-out layer. When one event has to reach 100,000 subscribers spread over 200 servers, the pub/sub system doing that routing is the bottleneck, and one popular channel makes it much worse. The second thing to break is deploys. Restarting means a million reconnections and logins within a few seconds, a spike far bigger than normal traffic and entirely self-inflicted. Third is memory, if I am buffering per-connection state or missed messages. So I would group connections by channel, so a server holds users interested in the same things. Keep per-connection state tiny. Put a hard cap on buffered messages per client and drop the slow ones instead of growing without limit. And make clients reconnect with randomised, growing delays. Cost: grouping by channel means a user in many channels touches several servers, which makes routing more complex.

6

Your turn

60-second self-check

A dashboard updates once a minute. Which transport, and why not the other two?

Stuck? Take one hint at a time0 of 2 used
0 / 25 characters
7

Learn it from the source

This page gets you interview-ready. These take you to where the idea is actually specified or measured — read them when you want the real depth.

Where you will use this