Skip to content
C Codeloom
Web

WebSockets vs Server-Sent Events

How WebSockets and SSE differ in transport, direction, reconnection, and operations, with guidance on when each one is the right pick for real-time web apps.

·4 min read · By Codeloom
Intermediate 9 min read

What you'll learn

  • What WebSockets and SSE actually are
  • When one-way push is enough
  • Reconnection and backpressure differences
  • Proxy, auth, and ops considerations
  • How to choose without overengineering

Prerequisites

  • Basic HTTP and JavaScript familiarity

Every few years a team picks WebSockets for a problem that only needed a one-way stream, then spends a quarter wrestling with sticky sessions and load balancer quirks. SSE is the unglamorous option that often fits better. Knowing which to reach for starts with seeing what each protocol actually is.

What and Why

WebSockets upgrade an HTTP connection into a long-lived, bidirectional, binary-or-text channel. After the handshake, either side can send a frame at any time. Server-Sent Events are plain HTTP responses that never close, streaming text/event-stream data from server to client only.

WebSockets shine when both sides talk: chat with typing indicators, collaborative editing, multiplayer games. SSE shines when the server has news and the client only needs to listen: live dashboards, notifications, AI streaming completions, build logs. Most “real-time” features in business apps fall in the second category and never need true bidirectional traffic.

Mental Model

A WebSocket is a phone call: both people can talk, and you have to manage the etiquette of who speaks when. SSE is a radio broadcast: the station transmits, you tune in. If you only need to listen, you do not need a phone.

The transport layer matters too. SSE rides on regular HTTP/1.1 or HTTP/2, so caches, proxies, gzip, and HTTP/2 multiplexing all work the way you expect. WebSockets use a distinct Upgrade handshake; every proxy, load balancer, and WAF in your path needs to understand it.

Hands-on Example

SSE on the server is a normal handler that flushes lines.

// Node, Express
app.get('/stream', (req, res) => {
  res.set({
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive',
  });
  const send = (data) => res.write(`data: ${JSON.stringify(data)}\n\n`);
  const t = setInterval(() => send({ t: Date.now() }), 1000);
  req.on('close', () => clearInterval(t));
});

The browser side is one line of API.

const es = new EventSource('/stream');
es.onmessage = (e) => console.log(JSON.parse(e.data));

A WebSocket version needs a separate server library and a different client API.

const ws = new WebSocket('wss://api.example.com/socket');
ws.onmessage = (e) => console.log(e.data);
ws.onopen    = () => ws.send(JSON.stringify({ subscribe: 'orders' }));
WebSocket:
client <===== full-duplex frames =====> server
     (single TCP, after HTTP Upgrade)
     binary or text, app-defined framing

SSE:
client <-----  text/event-stream  ----- server
     (single HTTP response, never closes)
     server -> client only
     browser auto-reconnects with Last-Event-ID
Transport comparison

SSE bakes in reconnection. If the connection drops, EventSource reconnects and sends a Last-Event-ID header so the server can resume. WebSockets leave reconnection to you, including exponential backoff and resubscribing to whatever the client was listening to.

Common Pitfalls

  • Picking WebSockets for one-way data. You inherit operational pain you do not need. If the client never sends meaningful frames, SSE is simpler.
  • Forgetting heartbeats. Idle connections get killed by NATs and load balancers after a minute or two. WebSockets need ping/pong; SSE needs periodic comment lines (: ping\n\n).
  • No backpressure handling. Both protocols will buffer until they fall over. Drop or coalesce on the server side when the client is slow.
  • Sticky sessions everywhere. Once you scale out, both protocols need either sticky routing or a shared pub/sub layer like Redis. Plan that on day one.
  • Auth in the URL. Tokens in WebSocket URLs end up in logs. Use a short-lived ticket or a cookie-based session.

Practical Tips

For AI streaming and notifications, start with SSE; it is one fetch handler and one EventSource. For collaborative editing and games, start with WebSockets and pick a library that handles reconnect and message acks. Limit message size on both ends. Add request IDs to every frame so you can correlate logs across services. Test through your real load balancer early; localhost will lie to you about timeouts and buffering.

If you are already on HTTP/2 or HTTP/3 everywhere, SSE benefits from multiplexing and modern flow control, removing the old “browser connection limit” argument against it.

Wrap-up

WebSockets and SSE solve overlapping but different problems. Ask “does the client need to talk back over the same channel” and let that answer pick the protocol. The boring choice is usually the right one, and the boring choice for one-way streams is SSE.