Server-Sent Events
- Pradeep P
- 3 days ago
- 3 min read
Series: Modern System Design · Layer 2 — Communication
Layer 2 · Post 9 of 10
← Previous: WebSockets → Next: Webhooks
Layer 2 — Communication · Post 24 of 88
Server-Sent Events stream updates from server to browser over a simple HTTP connection. One direction, low ceremony, good enough for many live UIs.
What you'll learn
How SSE differs from WebSockets and from polling
The text/event-stream format and last-event-id resume
Why LLM token streaming often looks like SSE
The idea in one minute
Server-Sent Events (SSE) is HTTP where the server keeps the response open and writes data: ... lines as things happen. The browser EventSource API reconnects for you.
It is one-way: server → client. The client still uses normal HTTP for sends.
That is enough for live scores, feed ticks, job progress, and streaming LLM tokens (Layer 6). You skip WebSocket handshake, custom ping protocols, and binary frames.
Why it matters
Not every live UI is a chat. Many products only need "tell me when this changes." SSE fits HTTP/1.1 and HTTP/2, plays nicer with some proxies than WebSockets, and is trivial to demo: curl the stream.
Limits: typically no binary, no client→server on the same socket, and some older proxies buffer the stream (you need X-Accel-Buffering: no style headers). Browsers cap concurrent SSE per domain (HTTP/1.1). HTTP/2 multiplexing helps.
How it works
Response headers: Content-Type: text/event-stream, no buffering, keep-alive.
Body:
id: 17 event: progress data: {"percent": 40}
data: hello world
Blank line ends an event. EventSource fires onmessage or named events.
If the connection drops, the browser reconnects and sends Last-Event-ID. Your server can resume from that id if you stored a log. That is the cheap version of "don't miss ticks."
Compared to WebSockets
: Direction; SSE: server → client; WebSocket: both
: API; SSE: EventSource; WebSocket: lower-level
: Protocol; SSE: HTTP; WebSocket: upgraded TCP frames
: Typical use; SSE: notifications, streams; WebSocket: chat, games, collab
You can mix: REST POST to send, SSE to listen.
A simple example
"Export CSV" job:
POST /exports → { "jobId": "abc" }
GET /exports/abc/events as SSE: queued → running → done with a download URL
The page never polls. If the user refreshes, Last-Event-ID or a GET of job status recovers.
LLM chat: POST /complete returns text/event-stream of tokens. Same idea, different payload.
Common mistakes
Buffering at nginx / CDN. The user sees nothing until the buffer fills. Disable proxy buffering for that route. CDNs may not be the right place for long streams.
No heartbeat. Intermediate timeouts close quiet streams. Send a comment line : ping every N seconds.
Using SSE for high-frequency binary. Use WebSockets or a dedicated protocol.
Forgetting auth on the GET. EventSource cannot set custom headers in the oldest browsers; people stuff tokens in query strings. Prefer cookie auth or a fetch-based SSE client.
How this shows up in real systems
OpenAI-style chat UIs: SSE or chunked HTTP for tokens.
GitHub / Twitter (historically): live comment/event streams.
Kubernetes kubectl logs -f: similar streaming, not always SSE.
Recap
SSE is one-way live HTTP. Simple, enough for many UIs.
Resume with event ids. Don't let proxies buffer.
Use WebSockets when the client must push continuously on the same connection.
The last common "call me when something happens" pattern is not a live socket at all. It is a webhook.
Series: Modern System Design · Layer 2 — Communication
Layer 2 · Post 9 of 10
← Previous: WebSockets → Next: Webhooks



Comments