Skip to main content
Web

WebSockets vs SSE vs long polling: pick the smallest real-time tool

SSE streams one-way server updates over plain HTTP, WebSockets open a two-way socket, long polling fakes both with repeated requests. Match the transport to the direction, not the hype.

Thien Nguyen
By Thien Nguyen
Updated July 21, 2026 · 3 min read

Reach for WebSockets by reflex and you often pay for a full-duplex socket to push data that only ever flows one way. Most "real-time" features — a live order status, a notification badge, a progress bar, tokens streaming out of an LLM — are the server telling the browser something, and nothing coming back on the same channel. For that shape, Server-Sent Events do the job over a single long-lived HTTP response: the browser opens an EventSource, the server keeps the connection open and writes data: lines, and the browser fires a message event for each one.

The reason SSE is underused is that it looks too simple to be a real-time transport. But it's specified in the HTML Living Standard, and the spec hands you two things WebSockets make you build yourself: automatic reconnection when the connection drops, and a Last-Event-ID header the browser resends on reconnect so the server can replay whatever the client missed. WebSockets, defined in RFC 6455, give you a raw bidirectional frame pipe and no opinion about any of that.

SSEWebSocketLong polling
DirectionServer → client onlyBidirectionalServer → client (per request)
TransportPlain HTTPUpgraded TCP socketPlain HTTP
Auto-reconnectBuilt inYou build itTrivial (just request again)
Missed-message replayLast-Event-ID, built inYou build itCursor in your own request
Binary payloadsNo (text/UTF-8)YesYes
Works through old proxiesUsuallySometimes blockedAlways
OverheadOne connectionOne connectionNew request each cycle

Long polling is the fallback, not a peer: the client sends a request, the server holds it open until there's data or a timeout, then the client immediately asks again. It works literally everywhere because it's just HTTP requests, which is exactly why it's the compatibility floor — reach for it when a corporate proxy strips the SSE stream or refuses the WebSocket upgrade, not as a default.

Every one of these gives you a byte pipe and nothing else. Ordering, delivery guarantees, refreshing an expired auth token mid-stream, and recovering the messages sent while the client was offline are your application protocol to design — the transport won't do it for you. This is the part teams skip, and it's the part that pages someone.

The honest default is to start with SSE and only upgrade to WebSockets when the client genuinely needs to send low-latency messages back on the same channel — collaborative editing, multiplayer, a chat where typing indicators matter. Picking the smaller transport isn't about saving bytes; it's that SSE's reconnect and replay are already written and tested, so choosing it means there's less of your own protocol left to get wrong at 3am.

Cover photo by Stanislav Kondratiev on Pexels.

References

Primary documentation and specifications checked when this article was last updated.

WebReal-timeAPIs

Related articles

All articles