Here’s the core problem. Plain HTTP is request-response — the client asks, the server answers, done. The server can’t just tap the client on the shoulder and say “hey, new message arrived”. So how do we build a live chat, a stock ticker, or a notifications badge?
In simple language — we need a way for the server to push data to the client instead of waiting to be asked. There are four common approaches, each a step up in real-time-ness.
Short polling — “are we there yet?”
The dumbest-but-simplest option. The client asks on a timer: every few seconds it fires a request, “anything new?” Server answers immediately, usually “nope”.
It works, but it’s wasteful. Most requests come back empty, and there’s always lag — a message that lands right after a poll waits until the next one. Lots of traffic, little payoff.
// short polling — hammer the server every 3s
setInterval(async () => {
const res = await fetch("/api/messages?since=" + lastId);
const msgs = await res.json();
render(msgs); // mostly empty responses
}, 3000);
Long polling — “hold the line”
Smarter. The client asks, but the server holds the request open until it actually has something to say (or a timeout hits). The moment there’s data, it responds. The client immediately opens a new request and waits again.
Same request-response model, but way fewer empty responses and near-instant delivery. It’s how a lot of “real-time” was done before WebSockets existed, and it’s still a solid fallback.
// long polling — server holds the connection until there's data
async function poll() {
const res = await fetch("/api/updates"); // may hang for ~30s
render(await res.json());
poll(); // immediately re-open
}
poll();
SSE — a one-way stream from the server
Server-Sent Events open a single long-lived HTTP connection over which the server streams messages one way: server → client. The client can’t send data back on that channel (it uses normal HTTP requests for that).
It’s built into browsers via EventSource, auto-reconnects for us, and rides over regular HTTP — so proxies and firewalls don’t complain. Perfect for feeds, notifications, live scores, progress bars.
// SSE — the browser handles reconnection for us
const es = new EventSource("/api/stream");
es.onmessage = (e) => render(JSON.parse(e.data));
// one-way: server pushes, we just listen
WebSockets — a two-way pipe
WebSockets give us a full-duplex connection — both sides can send messages any time, independently. In simple language: a real two-way phone call instead of walkie-talkie turns.
It starts life as an HTTP request that gets upgraded to the WebSocket protocol (ws:// or wss://), then stays open as a persistent TCP connection. Once upgraded, it’s no longer HTTP — just a raw message pipe both ways with tiny per-message overhead.
const ws = new WebSocket("wss://api.chat.com/room/42");
ws.onopen = () => ws.send("hello"); // client -> server
ws.onmessage = (e) => render(e.data); // server -> client, any time
The upgrade handshake
WebSockets sneak in through a normal HTTP request so they work everywhere HTTP works.
GET /room/42 HTTP/1.1
Host: api.chat.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
The server agrees with a 101 Switching Protocols, and from that point the same TCP connection carries WebSocket frames instead of HTTP.
Which one, when?
Here’s the whole picture — direction of data flow tells the story.
Rules of thumb:
- Only the server needs to push, one-way? → SSE. Simpler than WebSockets, rides plain HTTP, free reconnection.
- Both sides chatter constantly (chat, games, collab editing)? → WebSockets.
- Can’t use either / need a dead-simple fallback? → long polling.
- Short polling? Only for low-stakes stuff where a few seconds of lag is fine and you want zero moving parts.
Interview soundbite
HTTP is request-response, so the server can’t push on its own. Short polling asks on a timer (wasteful, laggy). Long polling holds the request open until there’s data (near real-time, good fallback). SSE is a one-way server→client stream over plain HTTP with auto-reconnect — great for feeds and notifications. WebSockets upgrade an HTTP connection (via Upgrade header → 101 Switching Protocols) into a persistent full-duplex pipe — use them when both sides need to send freely, like chat or multiplayer.