Skip to content
matthewcm.dev

    ↑ ↓ move · ↵ open · tab scopeopen graph →

    Server-Sent Events (SSEs)

    One-way server-to-client streaming over plain HTTP, with automatic reconnection built into the browser. The simplest right answer for feeds, progress updates and streaming AI responses.

    Growingtended planted 1 min read
    On this page

    Server-Sent Events give you a long-lived HTTP response that the server keeps writing to. The client listens; if it wants to send something back, it uses an ordinary request. That one-way restriction sounds limiting, but a surprising number of “real-time” features only need the server to talk.

    What it looks like

    The server responds with Content-Type: text/event-stream and writes small text frames:

    HTTP/1.1 200 OK
    Content-Type: text/event-stream
    
    id: 42
    event: progress
    data: {"pct": 60}
    

    And the browser has a built-in client:

    const es = new EventSource("/jobs/123/events");
    es.addEventListener("progress", (e) => setProgress(JSON.parse(e.data).pct));

    EventSource reconnects automatically and sends the last event ID it saw (Last-Event-ID), so the server can replay what was missed. With WebSockets you write all of that yourself.

    A pattern I like: respond fast, enrich later

    A user submits something that takes a while to process — say, an idea that needs analysing. Return a quick response immediately, then stream updates over SSE as background work finishes. The UI feels instant and gets richer as results arrive. It’s also exactly how most LLM chat interfaces stream tokens.

    Gotchas

    • Text only. Binary has to be encoded.
    • No custom headers on EventSource. Use cookies, or a fetch-based stream reader if you need an Authorization header.
    • Connection limits. Under HTTP/1.1, browsers allow around six connections per domain, which several open tabs can exhaust. HTTP/2 multiplexes streams and makes this go away.
    • Buffering proxies can hold the stream back. Disable response buffering, and send a comment line (: ping) every so often as a Heartbeat so idle timeouts don’t kill it.

    Choosing

    If the client only needs to receive, start with SSE. Reach for WebSockets when the client also sends frequently on the same channel. SSE is essentially the standardised, browser-supported version of HTTP Long Polling.

    • #sse
    • #real-time
    • #streaming
    • #http

    ↪ Links to (3)