Comparisons

Pairs that get conflated in real conversations, and in real pull requests. Neither column wins — what decides is the requirement. Each record leads with the confusion, because the confusion is the reason the record exists.

Server-Sent Events vs WebSocket

What people get wrong about this pair

WebSocket is chosen by default because it sounds like the realtime primitive, and then the application uses it for a feed that never sends anything upward. That choice is not free: you have left HTTP, so you have also left its infrastructure. SSE is a normal HTTP response, which means compression, HTTP caching semantics, standard authentication with cookies or headers, ordinary proxy and load-balancer behaviour, and automatic reconnection with a last-event-id all work without you writing them. WebSocket gives you none of those by default: reconnection, heartbeats, backpressure, message framing, authentication on the upgrade and a re-sync protocol are all yours to implement, and intermediaries handle long-lived upgraded connections less predictably. The other half of the confusion is that either transport solves ordering and duplicates. Neither does. Both need event ids, an idempotent apply step and a resynchronisation path after a gap, because a dropped connection loses events that no reconnect can replay.

SSE — a long-lived HTTP response streaming events server-to-client
Use it when

One-way updates: notifications, progress, live counters, streamed model output, a feed. The client's side of the conversation is ordinary requests.

WebSocket — a bidirectional, framed connection after an HTTP upgrade
Use it when

Genuine two-way, low-latency traffic where the client sends frequently as well: collaborative editing, multiplayer, live cursors, an interactive terminal.

DimensionSSE — a long-lived HTTP response streaming events server-to-clientWebSocket — a bidirectional, framed connection after an HTTP upgrade
DirectionServer to client onlyBoth directions
ProtocolPlain HTTP response with a streaming bodyHTTP upgrade, then its own framed protocol
ReconnectionBuilt in, with last-event-id resumptionYou implement it, plus heartbeats
AuthWhatever the origin already usesHandled on the upgrade; tokens in a URL are a common leak
PayloadText events, one field set per messageText or binary frames
Infrastructure friendlinessHigh — it is an HTTP responseLower — proxies, timeouts and LBs vary in how they treat it
Server costOne held connection per subscriberOne held connection per subscriber, plus its own state
Still your problemOrdering, duplicates, gap recoveryOrdering, duplicates, gap recovery — identically