Real-Time Updates: Long Polling vs SSE vs WebSockets
Long Polling vs SSE vs WebSockets
Web developmentweb development / real-time
The scenario: you need the browser to get live data — a notification badge, an order-status ping, a chat, a stock ticker. But HTTP is request→response; the server can't just "push" out of nowhere. Three ways to solve it. Picking the wrong one is a classic intermediate mistake.
Long Polling
Client sends a request; the server HOLDS it open until it has data (or times out), then responds. The client immediately re-requests.
- Transport: plain HTTP — works everywhere, through every proxy.
- Cost: each message = a whole new request (headers, TLS, routing).
- Use when: low message rate, max compatibility, can't add infra.
Server-Sent Events (SSE)
ONE long-lived HTTP response that streams events. Built into browsers.
- Direction: server → client only.
- Free auto-reconnect + resume via Last-Event-ID.
- Watch the ~6-connections-per-domain cap on HTTP/1.1 (HTTP/2 multiplexes and fixes it).
Minimal SSE:
const es = new EventSource('/updates');
es.onmessage = e => console.log(e.data);
Server writes chunks like data: hello\n\n with Content-Type: text/event-stream.
WebSockets
A single TCP connection UPGRADED (HTTP 101 Switching Protocols) into a full-duplex channel. Both sides send anytime.
- Direction: bidirectional, tiny per-message overhead (a 2–14 byte frame vs full HTTP headers).
- Not plain HTTP after the upgrade — needs ws://wss:// and sometimes proxy/LB config.
- You re-implement heartbeats + reconnect yourself.
Decision rule
- One-way, keep it simple → SSE.
- Two-way, chatty → WebSockets.
- Must cross hostile proxies / ancient clients → Long Polling.
What people get wrong
- Reaching for WebSockets for a notification badge. SSE is simpler and auto-reconnects; with WS you own the reconnect logic.
- Forgetting SSE's per-domain connection cap on HTTP/1.1: open 6 tabs = 6 streams, the 7th hangs. HTTP/2 removes the pain.
- Load balancers silently killing idle connections — BOTH SSE and WS need heartbeats/timeouts tuned or they die after ~60s.
Your exercise
You're building both a "download progress %" bar and a live chat in the same app. Which transport for each, and why? Bonus: for the SSE one, what breaks when a user opens 8 tabs on HTTP/1.1 — and how does switching to HTTP/2 change your answer?