AlgoMaster Logo

Server-Sent Events

Medium Priority11 min readUpdated September 26, 2026
AI Mock Interview

Practice this topic in a realistic system design interview

Listen to this chapter
Unlock Audio

Premium Video

This video is available to premium subscribers only

Unlock Full Access

Suppose you are building a metrics dashboard. The server receives new data continuously, and the dashboard should update whenever something changes.

What you really want is for the browser to connect once and let the server send new data whenever it becomes available. That is exactly what Server-Sent Events (SSE) provide.

Connection stays openGET /events200 OK, text/event-streamJob completedevent: job-completedata: {"jobId":"abc"}Alert createdevent: alertdata: {"severity":"high"}BrowserServerEvent ProducerBrowserServerEvent Producer
7 / 7
algomaster.io

This chapter covers how SSE works, what its events look like, how reconnection and replay work, how to scale it, the operational challenges of long-lived connections, and when to use it.

1. Why Do We Need SSE?

One option for the dashboard is polling. The browser sends a request every few seconds asking whether there is anything new.

This works, but it creates a trade-off:

  • Poll frequently, and you generate lots of requests, many of which return nothing.
  • Poll less frequently, and you reduce traffic, but updates arrive with more delay.

Long polling improves this, but every response still closes the request and forces the browser to ask again.

SSE removes the trade-off. The browser connects once, and the server pushes each update the moment it exists.

2. What Are Server-Sent Events?

Server-Sent Events let a server continuously push updates to a client over HTTP.

The client sends a normal HTTP request. Instead of returning a response and closing the connection, the server keeps the response open. Whenever new data becomes available, the server sends another event over that same connection, and the client receives it immediately.

The key limitation is that SSE is one-way. The server can continuously send data to the client, but the client cannot send messages back over the same connection. If the client needs to send data to the server, it uses a separate HTTP request, such as a normal POST.

For a dashboard, a feed, or a progress bar, that is fine: the server talks and the browser listens.

3. How an SSE Connection Works

In a browser, SSE is used through the EventSource API. The client connects to an endpoint exposed by the server:

The server responds with a special content type, text/event-stream. Instead of closing the response, it keeps the connection open and continues sending events over it.

Here is a small Node.js endpoint that does this:

EventSource moves through three states:

StateValueMeaning
CONNECTING0Connecting or reconnecting
OPEN1Stream is open
CLOSED2Stream was closed and will not reconnect

4. SSE Events

The SSE event format is intentionally simple. Each event is a few lines of text followed by a blank line:

Each event usually contains some data. That data might be plain text or JSON.

Events can also have a type, which lets the client tell different kinds of updates apart. The same stream could carry notification events, order status events, and progress events, and the client registers a separate listener for each.

Events can also have an ID. That becomes useful if the connection breaks and the client needs to continue from where it left off.

FieldPurpose
dataEvent data. Multiple data lines are joined with newline characters
eventEvent type. If omitted, the browser treats it as a generic message event
idPosition marker used for reconnecting through Last-Event-ID
retryReconnect delay in milliseconds suggested by the server

For multi-line data, repeat the data field:

So although the protocol is simple, it already includes the features a long-lived streaming connection needs.

5. Automatic Reconnection

One useful feature of SSE is automatic reconnection. If the connection drops, the browser automatically tries to reconnect, so you do not need to write basic retry logic yourself.

By default, browsers wait about 3 seconds before reconnecting. The server can suggest a different delay with the retry field:

But this raises another question: what happens to events sent while the client was disconnected?

6. Recovering Missed Events

SSE handles this with a mechanism called Last-Event-ID.

The server assigns an ID to each event, and the browser keeps track of the most recent one it received. If the connection drops, the browser reconnects and sends that ID in the Last-Event-ID header. The server uses it to work out where the stream left off.

For example, if the client last received event 100, the server can replay events 101 onward.

Connection dropsGET /eventsid: 99, data: ...id: 100, data: ...GET /eventsLast-Event-ID: 100Read events after 100Events 101 and 102id: 101, data: ...id: 102, data: ...BrowserServerEvent StoreBrowserServerEvent Store
9 / 9
algomaster.io

But SSE does not store those events for you. The browser remembers the last ID, and the server has to be able to look up what came after it. Reliable replay requires durable storage such as a database, cache, event log, or message broker.

So Last-Event-ID helps resume the stream, but durability is still the application's responsibility.

7. Scaling SSE

Now suppose there are multiple SSE servers behind a load balancer.

When a client opens an SSE connection, the load balancer routes it to one server, and the connection stays there until it closes. So different users end up connected to different SSE servers.

If a backend service generates an event for Bob, the system has to route that event to the server currently holding Bob's connection. The service that produced the event has no idea which server that is.

A common solution is a shared messaging layer:

  1. Backend services publish events.
  2. The messaging system distributes them to every SSE server.
  3. The server holding the right connection pushes the event to the client.

This keeps business services separate from the servers that manage long-lived connections.

8. Pub/Sub Behind SSE

The messaging layer could be Redis Pub/Sub, Kafka, or another event system, depending on your requirements.

  • Redis Pub/Sub works well for fast, transient events where replay is not important. A server that is not subscribed at the moment a message is published never sees it.
  • Kafka is a better fit when you need durability, replay, or large-scale event processing. Events stay in the log, so a reconnecting client's Last-Event-ID can be served from it.

Either way, the roles stay the same. Backend services produce events, the messaging layer distributes them, and SSE servers manage client connections and deliver updates. This separation makes the system easier to scale horizontally: add SSE servers for more connections without touching the producers.

9. Slow Clients and Backpressure

Another challenge is slow clients. If the server produces events faster than a client can consume them, buffered data for that client keeps growing and can eventually consume significant memory.

So SSE servers need a backpressure strategy: a rule for what to do when a client cannot keep up. Common options:

  • Buffer temporarily, up to a limit, to absorb short bursts.
  • Drop older events when the buffer is full.
  • Send only the latest state, when old values are no longer useful, such as a price or a progress percentage.
  • Disconnect the client and let it reconnect later, resuming from its Last-Event-ID.

10. Heartbeats and Infrastructure

SSE connections may stay quiet for long periods. Proxies, load balancers, and other infrastructure can close them as idle.

A common solution is to send periodic heartbeats. Lines that start with : are comments, which the browser ignores, so they make good heartbeats:

Heartbeats carry no meaningful data. They keep the connection alive and help detect failures. Send them more often than the shortest idle timeout along the request path.

The other concern is response buffering. SSE relies on streaming data through a long-lived HTTP response, but some proxies, CDNs, and middleware buffer response data before forwarding it. If that happens, an event the server sent immediately may reach the client several seconds later.

So the entire HTTP path must support streaming correctly, with compatible idle timeouts and buffering disabled where needed. In Nginx, for example, teams commonly send the X-Accel-Buffering: no header, as the server example above does.

11. Deployments and Connection Draining

Long-lived connections also affect deployments.

If an SSE server with thousands of active clients shuts down immediately, all those clients disconnect and may reconnect at once. That creates a sudden traffic spike on the remaining servers.

A better approach is connection draining:

  1. The load balancer stops sending new connections to the server being retired.
  2. Existing connections stay until they close naturally or reconnect elsewhere.
  3. Once the server has no active connections left, it shuts down safely.

Because clients resume with Last-Event-ID, moving to another server does not lose events.

12. When to Use SSE

SSE is a strong choice when you need frequent server-to-client updates without a fully bidirectional connection. Common use cases:

  • Notifications
  • Live dashboards and monitoring systems
  • Job progress
  • Sports scores
  • Social media feeds

If both the browser and the server need to send frequent messages on the same connection, WebSockets are a better fit.

13. SSE for Streaming AI Responses

A popular modern use case for SSE is streaming in LLM applications.

When a user sends a prompt, generating the full response may take several seconds. Instead of waiting for the entire answer, the server streams it as it is generated:

This makes the application feel much more responsive, because the user starts seeing results almost immediately.

It is a natural server-to-client streaming pattern: the client sends one request, and the server progressively sends the response back.

Summary

Polling forces a choice between wasted requests and delayed updates. SSE lets the browser connect once while the server pushes events over a long-lived HTTP response with the text/event-stream content type. It is one-way: anything the browser sends goes through separate requests.

Events are simple text with data, an optional type, and an optional ID. Browsers reconnect automatically and send Last-Event-ID, but replaying missed events needs durable storage on the server side.

To scale, a messaging layer such as Redis Pub/Sub or Kafka routes each event to the server holding the client's connection. In production, plan for slow clients with a backpressure strategy, keep quiet connections alive with heartbeats, disable buffering along the path, and drain connections during deployments.

SSE fits notifications, dashboards, job progress, feeds, and streaming AI responses: anywhere the server talks and the browser listens.

Quiz

Server-Sent Events (SSE) Quiz

12 quizzes