codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
Notes›backend

WebSocket and SSE — real-time communication

Published 2026-04-28· Updated 2026-05-18·0 views

WebSocket and SSE — real-time communication

HTTP is fundamentally a one-request-then-one-response model. It is hard for the server to speak to the client first. In places like chat, notifications, and live updates, the server must be able to push data actively.

1. About real-time communication

Event Time
Comet (parent concept of long polling) 2006
WebSocket first draft (HyBi) 2009
WebSocket RFC 6455 2011
EventSource (SSE) HTML5 standardization 2011 ~
HTTP/2 + Server Push (separate model) 2015 (usage declined afterward)

A bird's-eye view of the three candidates:

Model Direction Protocol Use
Short Polling Client driven Plain HTTP Simple, cost-inefficient
Long Polling Client driven, server holds Plain HTTP Fallback
SSE Server → client one-way HTTP (Content-Type: text/event-stream) Notifications, live feeds
WebSocket Bidirectional ws:// · wss:// (HTTP Upgrade) Chat, games, collaboration

2. Polling

Short Polling — the client polls on a fixed interval to ask for new data.

setInterval(() => fetch('/messages?after=' + lastId), 3000);

Strength is simplicity. Downsides are many empty responses (cost-inefficient) and latency tied to the polling interval.

Long Polling — the server holds the response until new data arrives. After the response, the client immediately polls again.

Client → /messages?after=42  ─────────────┐
                                          │ (server waits until a new message arrives)
Server ← 200 OK [{id:43,...}]  ←──────────┘
Client → /messages?after=43  ─────────────┐ (immediately starts the next poll)

This was the standard flow before WebSocket. It still remains as a fallback (older browsers, some proxy environments).

3. SSE

Standardized via the EventSource API and the text/event-stream MIME type. The server keeps streaming data over a single open HTTP connection.

GET /events
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

data: {"id":1,"text":"hi"}

event: ping
data: {}

id: 12
data: {"type":"update"}

The client opens it with the browser-native API.

const es = new EventSource('/events');
es.onmessage = (e) => console.log(JSON.parse(e.data));
es.addEventListener('ping', () => {});

When disconnected, EventSource auto-reconnects. The last received id is sent back as the Last-Event-ID header to help recover any missed events.

Characteristics:

  • One-way (server → client).
  • Works on HTTP/1.1 and HTTP/2. On HTTP/2, multiple SSE streams can share one connection.
  • Text based.
  • Plain HTTP, so it fits well with proxies and load balancers.

Frequently used for notifications, news feeds, and LLM token streaming.

4. WebSocket

Begins with the HTTP Upgrade header and switches to a separate protocol.

GET /chat
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: <base64>
Sec-WebSocket-Version: 13

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: <derived>

After that, frame-based bidirectional communication runs on the same TCP connection. Both text and binary are supported. ping/pong frames keep it alive.

Characteristics:

  • Bidirectional, low latency.
  • Being a separate protocol means some proxies or CDNs may not handle it (most do).
  • The connection state burden falls on the user (reconnection, sessions, auth).

Frequently used for chat, games, collaborative editing (Yjs · CRDT), and real-time market data.

5. Node — ws · Socket.IO

import { WebSocketServer } from 'ws';
const wss = new WebSocketServer({ port: 8080 });
wss.on('connection', (sock) => {
  sock.on('message', (m) => sock.send(`echo ${m}`));
});

Socket.IO layers automatic reconnection, rooms, and event emit on top of ws. The protocol differs slightly from standard WebSocket, so compatibility requires the Socket.IO client.

6. FastAPI · Spring WebFlux

from fastapi import FastAPI, WebSocket

app = FastAPI()

@app.websocket('/ws')
async def ws(s: WebSocket):
    await s.accept()
    while True:
        msg = await s.receive_text()
        await s.send_text(f"echo {msg}")

Spring WebFlux is WebSocket on top of Reactive Streams. Backpressure feels natural. Spring MVC also covers the same place via @MessageMapping (STOMP over WebSocket).

Most frameworks build SSE by streaming chunked responses. FastAPI uses StreamingResponse, Express uses a res.write loop, Spring WebFlux uses Flux<ServerSentEvent<...>>.

7. Managed services

Service Model Notes
Pusher (2010) Managed WebSocket Channel/event model. Rich SDKs.
Ably (2016) Managed messaging Globally distributed. Message guarantees.
Supabase Realtime Postgres changes → WebSocket DB change stream.
Firebase Realtime DB · Firestore Real-time DB The DB itself is a change stream.
Pub/Sub (Google) · SNS · EventBridge Event backbone More for backend-to-backend than direct client.
Centrifugo OSS WebSocket server Self-hostable managed alternative.

If we want to skip the burden of running it ourselves (connections, scaling, reconnection), managed services help. When PostgreSQL is already in place and we need change notifications, something like Supabase Realtime fits naturally.

8. Which model to choose

  • One-way, relatively low frequency, notifications/feeds — SSE. Plays well with HTTP infrastructure and recovers automatically.
  • Bidirectional, low latency, chat/games/collaboration — WebSocket.
  • Legacy proxies and old browsers — Long Polling fallback.
  • DB change notifications — Supabase Realtime · CDC + backend → SSE/WebSocket.

9. Authentication and scaling

The standard EventSource API cannot add headers. Token authentication is delivered via cookies or query strings. WebSocket is similar — authenticate at handshake time.

Putting tokens in the query string can leave them in access logs, so cookies are often recommended.

As connection count grows, a single instance hits memory and FD limits.

Distribute across instances
→ Sticky session, or fan out messages with Redis Pub/Sub

ALB · NLB support WebSocket
→ ALB idle timeout defaults to 60s, so keep alive with pings

10. Common pitfalls

idle timeout — ALB, CloudFront, and some proxies cut connections at 60 seconds to a few minutes. Keep alive with ping/pong on both client and server.

Duplicate connections — clients with badly-written reconnection logic hold two connections at once and receive every message twice.

SSE EventSource header limits — the standard EventSource API cannot add headers. Token authentication goes via cookies or query.

Message order and duplication — WebSocket is a fundamentally reliable channel, but loss is possible after reconnection. The server must offer last-id-based recovery.

Missing backpressure — if the server sends faster than the client consumes, memory and connections drain. Throttle in token-sized chunks.

CORS / Origin verification — WebSocket has different same-origin policy effects. Verify the Origin header during the handshake.

No TLS — ws:// is plaintext. In production, always wss://. There are reports of plaintext WebSocket being severed in some environments (public Wi-Fi).

Closing thoughts

There is an impression that WebSocket is the right answer for real-time, but SSE is often the better fit. When the flow is one-way, SSE's simplicity reduces operational work. WebSocket is safer to introduce only where bidirectionality is truly needed.

Next

  • typeorm-readonly
  • spring-multi-module

See RFC 6455 — WebSocket Protocol · HTML Living Standard — Server-Sent Events · WebSockets API (MDN) · ws · Socket.IO · FastAPI WebSocket · Spring WebFlux WebSocket · Pusher · Ably · Supabase Realtime.

More in backend

All in this category →
  • Wrap public OpenAPIs with your own BFF
  • Email Delivery and OTP — SMTP
  • Audit Log — logAdminAction pattern
  • REST API introduction
  • OpenAPI Specification
  • Crawler ethics and tooling