The default answer is wrong
When developers need to push data from server to browser in real time, the instinct is to reach for WebSockets. They're the "real-time" technology. But WebSockets are a bidirectional full-duplex protocol — and sometimes you simply don't need full duplex. Choosing the right tool starts with asking: who needs to talk, and in which direction?
What SSE actually is
Server-Sent Events (SSE) is an HTTP-based protocol where the server keeps a connection open and pushes newline-delimited text frames to the browser. The browser side is the native EventSource API. That's it. No upgrade handshake, no binary framing, no persistent socket state. Just a long-lived GET request with Content-Type: text/event-stream.
When SSE wins
SSE is the right choice when: - The flow is server → client only (live logs, notifications, progress bars, streaming LLM output) - You want automatic reconnect for free (EventSource reconnects on drop without any code) - You're behind a standard HTTP reverse proxy (Nginx, Caddy, AWS ALB — no special WebSocket config needed) - You want HTTP/2 multiplexing — SSE rides on the existing H2 connection, WebSockets require a separate socket For RootResume, streaming process stdout to the browser is a perfect one-way flow. SSE was the obvious choice.
When WebSockets win
WebSockets win when: - You need true bidirectional low-latency communication (multiplayer games, collaborative editing, chat with typing indicators) - You're sending binary data at high frequency (audio/video, binary game state) - You need sub-100ms round-trip where the HTTP overhead of SSE frames actually matters For a chat app, a multiplayer canvas, or a real-time collaborative IDE — use WebSockets.
The practical difference in Node.js
With SSE, the server side is a plain Express response handler:
js
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
process.stdout.on('data', chunk => {
res.write(`data: ${encode(chunk)}\n\n`);
});
With WebSockets you need an upgrade handler, a ws library, and to manage socket lifecycle separately from your HTTP server. More surface area, more failure modes.