WebSockets vs Server-Sent Events in 2026: pick the boring one
Most 'realtime' features are one-directional: notifications, live dashboards, streaming AI tokens. SSE handles all of them with plain HTTP. When you genuinely need WebSockets — and when you don't.
Somewhere along the way, 'realtime' became synonymous with WebSockets, and teams reach for them before asking the only question that matters: does data need to flow in both directions at low latency? For notifications, dashboards, activity feeds, and LLM token streams, the answer is no — the client sends nothing (or sends it fine over regular HTTP), and Server-Sent Events do the job with a fraction of the operational surface.
Why SSE is the boring, correct default
- It's plain HTTP: every proxy, load balancer, CDN, and corporate firewall already understands it. No Upgrade handshake to break.
- Automatic reconnection with Last-Event-ID is built into the EventSource API - with WebSockets, you write (and debug) that state machine yourself.
- It works with HTTP/2 and HTTP/3 multiplexing, so multiple streams share one connection instead of burning sockets.
- Auth is just your existing cookie or bearer token on a GET request - no ticket-based handshake dance.
// Server (Hono/any fetch-style runtime): stream events
app.get('/api/events', (c) => {
const stream = new ReadableStream({
start(controller) {
const send = (data: unknown, id?: string) =>
controller.enqueue(
`${id ? `id: ${id}\n` : ''}data: ${JSON.stringify(data)}\n\n`,
);
const unsubscribe = bus.subscribe(c.get('userId'), send);
c.req.raw.signal.addEventListener('abort', unsubscribe);
},
});
return new Response(stream, {
headers: {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
},
});
});
// Client: three lines, reconnection included
const es = new EventSource('/api/events');
es.onmessage = (e) => handleEvent(JSON.parse(e.data));When you genuinely need WebSockets
Bidirectional, low-latency, high-frequency: collaborative editing with live cursors, multiplayer games, trading interfaces, voice/video signaling. The test is whether the client sends messages frequently enough that HTTP request overhead per message actually hurts. A chat app where users send a message every few seconds does not pass this test — POST the message, stream the updates via SSE, and you've built 'realtime chat' with no WebSocket infrastructure at all.
The operational cost nobody prices in
WebSockets are stateful connections in a world of stateless infrastructure. Deploys sever every connection unless you drain gracefully. Horizontal scaling needs pub/sub plumbing (Redis, NATS) so a message published on server A reaches a socket held by server B. Serverless platforms need Durable Objects or a managed layer like Ably or Pusher. None of this is exotic anymore — but it's real engineering budget, and SSE spends almost none of it because each stream is just a long-lived HTTP response behind your existing load balancer.
The 2026 wrinkle: LLM streaming made SSE mainstream again
Every major AI API streams tokens over SSE, which means every AI-adjacent codebase now has EventSource handling in it anyway. If your product streams model output and also needs notifications or live dashboard updates, you already have the pattern, the reconnection logic, and the team familiarity. Reuse it. Reach for WebSockets when the requirements — not the vibes — demand full duplex.
Written by Appesto Engineering.