Real-time architecture discussions often reduce the decision to one question: do you need two-way communication? That is a good first filter, but production systems are decided by what happens when connections fail, instances restart, proxies time out, and clients miss events.
SSE is an HTTP response that stays open
Server-Sent Events send UTF-8 text from server to browser over a normal HTTP connection. The browser exposes a small EventSource API and automatically reconnects. Events can include an id, allowing the client to send Last-Event-ID after a disconnect so the server can resume from a known position.
const stream = new EventSource('/api/deployments/stream')
stream.addEventListener('status', event => {
updateDeployment(JSON.parse(event.data))
})
That makes SSE excellent for notifications, job progress, dashboards, and AI token streaming where the client already uses HTTP requests to send commands. It also fits existing authentication, observability, and proxy infrastructure more naturally than a separate protocol upgrade.
WebSockets create a bidirectional session
A WebSocket is appropriate when the server and client both send frequent low-latency messages: collaborative editing, multiplayer state, presence, or an interactive terminal. The protocol gives you frames and a connection—not message durability, retries, ordering across reconnects, or authorization rules. Those remain application responsibilities.
Design the reconnect path first
Both transports disconnect. Mobile devices change networks, load balancers rotate targets, and deployments terminate instances. A reconnecting client needs a way to recover state. Sequence numbers, resumable offsets, or a fresh snapshot followed by live events are architectural requirements, not optional polish.
A common design stores durable domain events in a broker or database and treats the socket server as a delivery edge. The edge can disappear without becoming the system of record. Clients reconnect, present their last acknowledged position, and receive either missed events or a new snapshot.
Infrastructure changes the answer
Check idle timeouts, maximum connection duration, connection quotas, and whether your platform supports graceful draining. Long-lived connections also change autoscaling: request rate becomes less useful than active connections, outbound messages, and per-instance memory.
I default to SSE for server-to-browser updates because it is simpler and has useful browser reconnect semantics. I choose WebSockets when the product truly needs a bidirectional session. In both cases, the important design is the recovery protocol around the connection—not the connection itself.