HTTP is built as question-and-answer: the client asks, the server replies. But many tasks point the other way: a chat message arrived, an order status changed, a price updated — the server must tell the client. Plain HTTP can't, and over the years three working approaches emerged: long polling, Server-Sent Events and WebSocket.

Long polling: a stretched question

The simplest idea: the client asks "any news?", and the server doesn't answer until news appears (or a timeout, say 30 seconds, expires). On receiving the response the client immediately asks again.

Pros: works over plain HTTP wherever HTTP works — any proxies, balancers, firewalls. Cons: permanently hanging requests consume resources, there is a gap between an answer and the next request (an event will wait), and every cycle carries full HTTP headers. Today it is the fallback — for environments where nothing better gets through.

Server-Sent Events: a one-way stream

SSE is part of the web standards: the client opens a regular HTTP connection, the server keeps it and writes events as a text/event-stream text flow:

data: {"orderId": 42, "status": "SHIPPED"}

data: {"orderId": 43, "status": "PAID"}

Key properties:

  • one-directional — events flow only from the server; the client sends its requests over ordinary HTTP alongside;
  • reconnection is built in: the browser's EventSource restores the connection itself and passes Last-Event-ID to catch up on what was missed;
  • it is still HTTP: compression, header-based authorization, proxies — all standard.

SSE is the underrated option: for "server notifies client" (statuses, feeds, operation progress, prices) it closes the task noticeably cheaper than WebSocket.

WebSocket: full duplex

WebSocket starts as an HTTP request with an Upgrade: websocket header; after the handshake the connection becomes a persistent bidirectional channel: both sides send messages whenever they want, with no HTTP headers per message.

It is the only one of the three with true two-way communication and minimal per-message overhead — and the price matches:

  • it is a different protocol: the whole path must support it (proxies, balancers, idle timeouts);
  • reconnection, restoring subscriptions and delivering missed messages are your job (which is why a higher-level protocol usually lives on top: STOMP, socket.io and the like);
  • thousands of persistent connections are their own load story on the server.

Comparison and choice

Long pollingSSEWebSocket
Directionserver → clientserver → clientboth
Transportplain HTTPplain HTTPown protocol after Upgrade
Reconnectioninherentbuilt in (EventSource)by hand / library
Overheadhighlowminimal
When to takehostile environmentsnotifications, feeds, progresschat, games, co-editing

The rule: need server push — start with SSE; need a dialogue (the client writes often: chat, a cursor in a shared document, games) — WebSocket; nothing gets through — long polling.

Scaling: the shared trap

Persistent connections break the familiar "any request to any instance" model: a connection lives on a specific server, while an event for that client may be born on another. The standard answer is a shared bus: instances publish events to a broker or Redis Pub/Sub, and each delivers them to its own connected clients. You will also have to make the load balancer friendly to long-lived connections: idle timeouts, and client-to-instance affinity if needed.

And remember reliability: every persistent connection breaks — the question is when, not if; reconnection and catch-up are designed from day one.

In short

  • Long polling is a request that waits for an event; works everywhere but is costly, with gaps between cycles — the fallback option.
  • SSE is a one-way event stream over plain HTTP with built-in reconnection; the best start for "server notifies client".
  • WebSocket is a bidirectional channel with minimal overhead; taken for dialogue scenarios, paid for with your own reconnection and delivery plumbing.
  • Scaling persistent connections goes through a shared event bus between instances; breaks and catch-up are part of the design, not an accident.
  • HTTP: how the protocol works — the foundation all of this rides on.
  • Load balancing — long-lived connections and the balancer.
  • Network reliability — timeouts, retries and living with breaks.