April 2026 update: if you are choosing a browser-friendly real-time transport today, the default rule is still simple: use SSE for server-to-client streams, WebSockets for two-way interaction, and polling only when simplicity or infrastructure constraints matter more than immediacy. WebTransport is finally becoming broadly available, but it is a separate, lower-level choice, not a drop-in replacement for these three.
TL;DR
- Short polling is the simplest option: the client asks every N seconds. It is easy to ship, easy to debug, and often good enough for admin dashboards or background status checks.
- Long polling keeps each HTTP request open until data is ready or a timeout fires. It feels more real-time than short polling, but it is still a reconnect loop with more HTTP overhead than a true stream.
- SSE (
EventSource) is usually the best default for server -> browser updates like notifications, logs, token streaming, job progress, and live feeds. - WebSockets are the right choice when both sides need to talk frequently with low latency, or when you need binary frames, presence, typing indicators, collaborative cursors, or game-style interaction.
- In 2026, classic
WebSocketis still the mainstream browser API, but it still does not provide backpressure, so you need application-level queueing and slow-consumer protection. - In 2026, SSE remains mature and broadly supported, and HTTP/2 largely removes the old pain of the browser's six-connection-per-origin limit that mattered more under HTTP/1.1.
What is the difference between polling, SSE, and WebSockets?
- Polling means the client keeps asking, "Anything new?" on a schedule.
- SSE means the client opens one HTTP response and the server keeps streaming text events down it.
- WebSockets mean the client and server open a persistent full-duplex channel and either side can send messages at any time.
If you only need the server to push updates into the browser, SSE is usually simpler than WebSockets. If the browser also needs to push frequent low-latency messages back, WebSockets win. If the data only needs to refresh every few seconds, polling is often enough and may be the cheapest option to operate.
First: when people say "polling," they often mean two different things
Short polling
The browser requests /updates every few seconds.
GET /updates -> response
wait 5s
GET /updates -> response
wait 5s
...
Good when:
- a 2-10 second delay is fine
- simplicity matters more than freshness
- you want normal HTTP behavior end to end
Long polling
The browser requests /updates, but the server holds the request open until new data exists or a timeout fires. The client reconnects immediately after every response.
GET /updates -> server waits -> response with data or timeout -> reconnect
Good when:
- you need near-real-time updates
- you still want to stay inside ordinary HTTP request/response patterns
- your stack or intermediaries make SSE or WebSockets awkward
Long polling is the only flavor of polling that belongs in most serious "real-time" comparisons.
SSE vs WebSockets vs Polling at a glance
| Option | Direction | Latency | Overhead | Binary | Browser API | Best fit | Main downside |
|---|---|---|---|---|---|---|---|
| Short polling | Client asks repeatedly | Seconds, by interval | High | Via normal HTTP payloads | fetch() | Simple status checks, low-frequency dashboards | Wasted requests and slower freshness |
| Long polling | Mostly server -> client, via held response | Low to moderate | Medium to high | Via normal HTTP payloads | fetch() | "Realtime enough" without persistent protocols | Reconnect churn and more app complexity than it looks |
| SSE | Server -> client only | Low | Low | No, text/UTF-8 only | EventSource | Notifications, logs, AI streaming, job progress, live feeds | One-way only |
| WebSocket | Two-way | Very low | Low | Yes | WebSocket | Chat, collaboration, games, frequent client/server messaging | Stateful connection handling and backpressure concerns |
Fast decision rule
- Use short polling when a few seconds of staleness is acceptable.
- Use long polling when you need faster updates but want to stay inside ordinary HTTP request/response patterns.
- Use SSE when the browser mostly listens and the server mostly talks.
- Use WebSockets when the browser and server both talk often, or when you need binary frames.
If you are building live notifications, token streaming, logs, dashboard refreshes, deployment output, or background job progress, start with SSE.
If you are building chat, presence, collaborative editing, multiplayer, cursor sync, or device control, start with WebSockets.
Polling: still useful, still boring, sometimes still correct
Polling is not dead. It is just expensive if you misuse it.
Why teams still choose polling
- It reuses the same auth, caching, observability, and load-balancer path as the rest of your HTTP API.
- It behaves predictably through proxies, CDNs, gateways, and enterprise networks.
- It is easy to scale because each request is short-lived and mostly stateless.
- It is usually easier to retry, rate-limit, and debug than long-lived connections.
Where polling works well
- admin panels
- back-office tools
- order or status pages
- batch job completion checks
- integrations where 5-30 second freshness is fine
What usually goes wrong
- everyone polls on the same interval and creates mini traffic spikes
- the interval is too aggressive for the actual business need
- each poll refetches a huge payload instead of using
sinceor cursor-based diffs
Practical polling rules
- poll only when the view is visible if possible
- add jitter to avoid herd effects
- request deltas with
since, version numbers, or cursors - back off when the tab is backgrounded or errors repeat
SSE: the best default for one-way browser streaming
SSE is just streaming HTTP with a browser-native client API: EventSource.
Why SSE is attractive
- The API is simple.
- Browsers automatically reconnect.
- The event format supports
event:,data:,id:, andretry:. - The browser can send
Last-Event-IDon reconnect, which makes resumable streams much easier. - Because it is still HTTP, SSE tends to fit existing auth, routing, and observability better than WebSockets.
Where SSE fits especially well in 2026
- live notifications
- audit or activity feeds
- build logs and deployment logs
- AI response streaming and token-by-token UIs
- job progress or queue progress
- pricing ticks or metrics dashboards where the client mostly consumes
Important SSE limitations
- It is one-way. The browser receives events, but it does not send messages back on the same connection.
- The native
EventSourceAPI is intentionally narrow: URL plus optionalwithCredentials, which is one reason it is simpler but also less flexible. - The stream is UTF-8 text, not binary.
- Under HTTP/1.1, browsers still have a low per-browser/per-domain connection limit for SSE. MDN notes the old limit of about 6 connections when not using HTTP/2.
- Under HTTP/2, that connection-limit problem is much less severe because stream concurrency is negotiated and is typically much higher.
SSE production notes
- set
Content-Type: text/event-stream - disable proxy buffering where needed
- send heartbeat comment lines like
:keepalive - include event IDs if resume matters
- use HTTP 204 if you intentionally want the browser to stop reconnecting
WebSockets: the right choice when interaction matters
WebSockets give you a persistent, full-duplex channel between browser and server.
Why teams choose WebSockets
- both sides can send at any time
- latency is low
- binary frames are supported
- the model fits chat, presence, collaborative apps, gaming, and device control naturally
Where WebSockets are the better fit
- chat and typing indicators
- collaborative editing or shared cursors
- live whiteboards
- multiplayer or game-style state sync
- browser-based remote control or terminal-like apps
- high-frequency bidirectional control messages
The 2026 reality check on WebSockets
- The standard
WebSocketAPI is mature and broadly supported. - The standard browser API still does not provide built-in backpressure.
WebSocketStreamexists but is still non-standard and only supported in one rendering engine, so most teams should not build their core architecture around it.- WebSockets can run over newer HTTP versions too, but you should still test your exact browser, server, proxy, and CDN path instead of assuming every intermediary behaves the same way.
WebSocket production notes
- check
Originduring the handshake - use
wss://in production - implement ping or pong or app-level heartbeats
- cap per-client queues
- use
bufferedAmountor your own queue depth metrics to detect slow consumers - reconnect with exponential backoff and jitter
Minimal code examples
Short polling
Client
let cursor = 0;
async function refresh() {
const res = await fetch(`/api/updates?since=${cursor}`);
const items = await res.json();
for (const item of items) handle(item);
cursor = items.at(-1)?.id ?? cursor;
}
setInterval(() => {
refresh().catch(console.error);
}, 5000);
Long polling uses the same shape on the client side, but the server holds the request open until it has data or reaches a timeout.
SSE
Client
const es = new EventSource("/api/stream");
es.addEventListener("progress", (event) => {
renderProgress(JSON.parse(event.data));
});
es.onmessage = (event) => {
handle(JSON.parse(event.data));
};
es.onerror = (err) => {
console.error("SSE failed", err);
};
Server (Node/Express)
app.get("/api/stream", (req, res) => {
res.setHeader("Content-Type", "text/event-stream");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Connection", "keep-alive");
const send = (event, data, id) => {
if (id) res.write(`id: ${id}\n`);
if (event) res.write(`event: ${event}\n`);
res.write(`data: ${JSON.stringify(data)}\n\n`);
};
const heartbeat = setInterval(() => {
res.write(":keepalive\n\n");
}, 15000);
const unsubscribe = bus.on("progress", (msg) => {
send("progress", msg, String(msg.id));
});
req.on("close", () => {
clearInterval(heartbeat);
unsubscribe();
});
});
WebSockets
Client
const ws = new WebSocket("wss://example.com/ws");
ws.addEventListener("open", () => {
ws.send(JSON.stringify({ type: "join", roomId }));
});
ws.addEventListener("message", (event) => {
handle(JSON.parse(event.data));
});
ws.addEventListener("close", () => {
retryLater();
});
Server (Node + ws)
import { WebSocketServer } from "ws";
const wss = new WebSocketServer({ server: httpServer, path: "/ws" });
wss.on("connection", (socket) => {
socket.on("message", (buffer) => {
const msg = JSON.parse(buffer.toString());
routeMessage(socket, msg);
});
const ping = setInterval(() => {
socket.ping();
}, 30000);
socket.on("close", () => clearInterval(ping));
});
What should common products use?
| Product or feature | Best starting choice | Why | |---|---|---| | Admin dashboard refreshing every few seconds | Short polling | Simple, cheap, and usually good enough | | Background job progress | SSE | One-way updates with low ceremony | | AI text streaming in the browser | SSE | Natural fit for server-to-client token streams | | Notifications tray | SSE | Push-only and easy to resume | | Team chat | WebSocket | Messages flow both directions constantly | | Collaborative editor | WebSocket | Frequent low-latency bi-directional updates | | Terminal-like browser session | WebSocket | Interactive and stateful | | Legacy environment that dislikes long-lived streams | Long polling | Near-real-time while staying inside classic HTTP |
Does HTTP/2 or HTTP/3 change the answer?
Yes, but not in the way people sometimes hope.
- SSE benefits immediately from HTTP/2 and HTTP/3 because it is still just HTTP streaming. If you also want a refresher on the transport layer itself, see HTTP/2 vs HTTP/3.
- WebSockets can also run over newer HTTP versions, and the relevant standards exist for HTTP/2 and HTTP/3, but real-world behavior still depends on the exact browser, server, proxy, gateway, and CDN combination you deploy.
- The main choice is still directionality, not protocol fashion: one-way stream, or true two-way messaging?
Production checklist
- Choose a cursor, sequence, or event ID model before shipping reconnect logic.
- Add heartbeats for any long-lived connection.
- Load-test through the actual proxy or CDN path, not just localhost.
- Track open connections, reconnects, dropped messages, and queue depth.
- Treat real-time endpoints like normal APIs: auth, origin checks, and rate limits still matter.
- For related background reading, see CORS explained without cargo culting and rate limiting strategies.
FAQ
Is SSE better than WebSockets?
Only when your traffic is mostly server -> browser. For notifications, logs, AI streaming, and job progress, SSE is usually simpler and cheaper to operate. For chat, collaboration, or frequent upstream client messages, WebSockets are the better fit.
Is polling outdated in 2026?
No. Polling is still the right answer when your freshness requirement is loose, your infrastructure is plain HTTP-centric, or the business case does not justify long-lived connection complexity.
Should I use SSE or WebSockets for AI streaming?
For most browser AI products, start with SSE. It matches the common "server streams tokens to the UI" shape well. Reach for WebSockets only if the same session also needs low-latency upstream events, shared presence, or richer two-way state sync.
Can SSE replace WebSockets for chat?
Not cleanly. You can fake chat with SSE plus separate POST requests, but once you need typing indicators, presence, optimistic delivery, or richer session state, WebSockets are usually the cleaner design.
Does HTTP/2 or HTTP/3 remove the need to choose carefully?
No. They improve transport behavior, especially for SSE, but the core question stays the same: do you need one-way streaming, or true two-way messaging?
Final recommendation
If you are unsure:
- If a few seconds of delay is acceptable, use polling.
- If the browser mainly needs to listen, use SSE.
- If the browser and server both need to talk frequently, use WebSockets.
That rule is still the right default in April 2026.