The Problem

HTTP is request-response. The browser asks, the server answers. But sometimes the server has news the browser needs to know about: a new message, a price change, a notification. How do you push from server to client?

Three patterns. Each has its time and place.

1. Polling (Short Polling)

Browser asks every N seconds: "anything new?" Most of the time the answer is "no."

Pros: trivially simple. Works with any HTTP server.
Cons: wasteful. 95% of requests return nothing useful. Updates are at most N seconds late.

Use it when: updates are rare, latency tolerance is high, simplicity matters most.

2. Long Polling

Browser sends a request. Server holds it open until something happens (or a timeout). Then it responds. Browser immediately makes another request to wait for the next event.

Pros: works over normal HTTP. Near-real-time when events arrive.
Cons: each event is a full HTTP request/response. Lots of connection churn. Servers must hold many open connections.

Use it when: can't use SSE or WebSockets, but want lower latency than short polling.

3. Server-Sent Events (SSE)

One HTTP connection, kept open, server streams events down it. The browser uses the EventSource API to read them. Standard, simple, built into browsers.

// Browser
const events = new EventSource('/events');
events.onmessage = (e) => console.log(e.data);

// Server (response stream)
data: {"type": "new_message", "id": 42}

data: {"type": "price_update", "value": 99.99}

Pros: standard HTTP, works with HTTP/1.1, HTTP/2, HTTP/3. Auto-reconnect built into the EventSource API. Simpler than WebSockets.
Cons: server-to-client only (one direction). Limited to text data. Some older browsers / proxies don't handle long-lived connections gracefully.

Use it when: you only need server-to-client streaming. Notifications, dashboards, live counters, AI streaming responses (ChatGPT uses SSE).

4. WebSockets

Full bidirectional persistent connection. Starts as HTTP, upgrades to WebSocket via the Upgrade: websocket header. After the handshake, both sides can send messages whenever.

// Browser
const ws = new WebSocket('wss://example.com/ws');
ws.onmessage = (e) => console.log(e.data);
ws.send('hello');

Pros: truly bidirectional. Lowest overhead per message after the handshake. Binary data supported. Real-time.
Cons: not just HTTP, so some proxies and load balancers need special config. No standardized auto-reconnect. More complex than SSE.

Use it when: you need true bidirectional real-time. Chat, multiplayer games, collaborative editing, live trading.

Comparison

Short PollingLong PollingSSEWebSockets
DirectionClient to serverClient to serverServer to clientBidirectional
LatencyHighLowLowLowest
Connection costHighHigh (re-open)Low (one stream)Lowest
ReconnectionN/AManualAutoManual
Binary dataYes (HTTP)YesNo (text only)Yes
HTTP semanticsYesYesYesNo (after upgrade)

How to Decide

Need bidirectional? WebSockets.
Server-to-client only, simple? SSE. Underrated and probably right for most "live updates" cases.
Updates rare, latency okay? Polling.
Have to use HTTP only, no streaming? Long polling.

Operational Concerns

Many open connections: WebSockets and SSE keep connections open per user. A million users equals a million connections. Need event-loop-based servers (Node, Go, async Python). Traditional thread-per-connection servers won't scale.
Load balancers: must support sticky sessions (a user must keep talking to the same server) or pub/sub between servers (broadcast events to all servers, each fans out to its connected clients).
Proxies: some corporate proxies kill long-lived connections. SSE handles reconnect automatically; WebSockets need application-level reconnect logic.
Heartbeats: NATs and firewalls drop idle connections. Send periodic pings.

Don't Forget WebRTC

For peer-to-peer browser-to-browser communication (video calls, file transfer), WebRTC is the right tool. It's UDP-based with NAT traversal. Different problem space than WebSockets but worth knowing about.

The One Thing to Remember

For most "the server has news" use cases, SSE is the underrated right answer. Simpler than WebSockets, more efficient than polling, built into browsers, automatic reconnect. Reach for WebSockets when you need bidirectional. Use polling only when you can't stream. Don't use long polling unless legacy constraints force it.