You’re building a live dashboard. You want updates to appear as they happen; like a new chat message, a stock price change, or someone typing in real time. The old method? Refreshing the page over and over. The better way? SSE, or Server-Sent Events.

SSE gives your server a way to push updates to the browser automatically. No polling. No constant checking. No websockets unless you truly need two-way traffic. Just simple, one-way, real-time streaming from server to client.

What Is SSE?

SSE stands for Server-Sent Events. It’s a browser-based technology that lets your server send automatic updates to your frontend over a single, long-lived HTTP connection.

In plain terms:

  • You open a connection from the browser
  • The server keeps it open and keeps sending new data as it happens
  • The browser listens and reacts, instantly

You don’t need to keep asking the server if something has changed. SSE makes the server do the talking.

‍{{cool-component}}‍

What Makes SSE Different?

You might be wondering, “Isn’t this just like WebSockets?”

Not quite. WebSockets allow two-way communication; both client and server can talk. But SSE is one-way only. That sounds like a limitation, but it actually makes things easier.

Here’s why you might choose SSE:

  • You only need the server to send updates (e.g. notifications, logs, progress updates)
  • You want something simple that works over HTTP/2 and HTTP/1.1
  • You want automatic reconnects and built-in retry handling
  • You don’t want to deal with custom protocols like WebSocket frames

SSE uses EventSource, a native browser interface that works in most modern browsers.

How to Find SSE in Action

If you’ve ever used:

  • A news ticker that updates in real time
  • A build log in a CI/CD pipeline
  • Server monitoring tools with live data

There’s a good chance you’ve already seen server side events in action. To spot them in the wild, open your browser’s DevTools, go to the Network tab, and filter by EventStream. That’s the MIME type used by SSE.

The browser keeps that stream open and updates the frontend without ever reloading the page.

How Does SSE Work?

Let’s walk through.

1. Frontend Creates a Connection

In your JavaScript, you create an EventSource:

const eventSource = new EventSource('/events');

That URL is where your server will stream updates from.

2. Server Responds with a Stream

The server sends data like this:

data: Hello from the server
data: Another update coming in
data: User X joined the chat

Each message is separated by a double newline. The browser sees this and fires a message event every time:

eventSource.onmessage = function(event) {

  console.log('New message:', event.data);

};

3. Browser Handles Reconnects

If the connection drops, the browser tries to reconnect automatically. You don’t have to write retry logic yourself unless you want to customize it.

What Does an SSE Stream Actually Look Like?

The format of an SSE stream is simple, but there are rules.

Every event is made of text lines sent over a long-lived HTTP connection. Each event ends with a double newline (\n\n), which tells the browser, “This message is done.”

Here’s what a basic event looks like:

data: Hello from the server

That will trigger a message event on the client with "Hello from the server" as the payload.

But SSE supports more than just data:. You can also use:

  • event: to name the event
  • id: to tag the event with an ID (used for reconnecting)
  • retry: to tell the browser how long to wait before trying again

Here’s a more advanced example:

id: 42
event: userJoined
data: {"username":"jane"}

This lets your frontend do more than just receive raw data; it can handle different event types cleanly:

eventSource.addEventListener("userJoined", function(e) {
  const user = JSON.parse(e.data);
  console.log(`${user.username} just joined`);
});

If the connection drops, the browser sends Last-Event-ID: 42 in the next request. The server can then resume where it left off. That’s built-in reliability without extra setup.

How to Calculate SSE Load or Bandwidth

Now let’s talk scale. What if you’re sending thousands of updates per second?

There’s no exact formula for SSE like you’d find in statistics, but you can calculate expected load using this pattern:

SSE bandwidth = (avg message size in bytes) × (messages per second) × (connected clients)

For example:

  • Each message is ~100 bytes
  • You send 2 updates per second
  • You have 500 clients

100 × 2 × 500 = 100,000 bytes/sec = ~100 KB/sec total bandwidth

This helps you find your SSE ceiling before things break under load.

How to Implement SSE on the Server

Here’s a quick example using Node.js and Express:

app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');

  res.write('data: Connected\n\n');

  const interval = setInterval(() => {
	res.write(`data: ${new Date().toISOString()}\n\n`);
  }, 1000);

  req.on('close', () => {
	clearInterval(interval);
	res.end();
  });
});

This is a working SSE endpoint. You’re sending the current time every second. You can swap in live messages, logs, or anything else you want.

‍{{cool-component}}‍

Is SSE a Push or a Stream?

It’s easy to call SSE a “push” technology, but technically, it’s not pushing in the way some protocols do.

SSE is a one-way stream. The browser opens an HTTP connection, and the server keeps writing to that response over time. It’s just one very long HTTP response that never fully finishes.

Each update you send is like writing another chunk into that stream. The browser listens and parses it line by line.

So while it feels like a push, it’s actually a client-initiated stream. That means:

  • The connection starts from the frontend
  • The server never initiates anything on its own
  • Proxies, CDNs, and load balancers must allow long-lived streaming

This helps you debug weird cases where something in the middle (like NGINX or Cloudflare) starts buffering or closing the connection.

Pros and Cons of SSE

Let’s make it clear when you should use SSE, and when you shouldn’t.

Pros:

  • Simple HTTP-based setup
  • Built-in retry and event handling
  • Low overhead compared to polling
  • Works out of the box in most browsers

Cons:

  • One-way only (no client-to-server messages)
  • Doesn’t work in older browsers like IE
  • Can be tricky with load balancers and proxies if not configured properly

SSE vs Polling vs WebSockets

If you only need server side events, SSE is usually better than both.

Feature SSE Polling WebSockets
Direction Server to client only Client repeatedly requests Full two-way
Overhead Low High Low (after handshake)
Built-in Reconnects Yes Not needed Manual implementation
Browser Support Good Universal Good
Use Case Logs, live updates Simpler but inefficient Real-time chat, games

How to Keep SSE Connections Alive

By default, many servers or proxies (like NGINX) close idle HTTP connections after a set time. That can break SSE.

To fix this:

  • Send a “heartbeat” message every few seconds (even if it's just data: ping)
  • Increase the keepalive_timeout in your server/proxy settings
  • Use retry: in the SSE stream to suggest reconnect intervals

retry: 10000

data: Keep alive

That tells the browser to wait 10 seconds before trying again after a drop.

SSE and CORS (Cross-Origin Requests)

If your frontend is hosted on one domain (like frontend.com) and your SSE server lives on another (like api.backend.com), you’re dealing with cross-origin requests. And SSE respects CORS rules.

So yes; SSE requires proper CORS headers, just like fetch() or XHR.

Here’s what your server needs to include in its response headers:

Access-Control-Allow-Origin: *

Content-Type: text/event-stream

That wildcard (*) works fine for public data. If you’re dealing with private or restricted content, set a specific origin instead:

Access-Control-Allow-Origin: https://your-frontend.com

Also remember:

  • You don’t need Access-Control-Allow-Methods or Access-Control-Allow-Headers for SSE
  • EventSource never sends credentials (cookies, tokens) unless you explicitly use { withCredentials: true } (which only works if the server allows it)

Getting these headers right is the difference between seeing your stream flow, or watching it silently fail.

When to Avoid SSE

SSE is great, but not perfect. You should avoid it if:

  • You need two-way interaction between client and server
  • You’re building a real-time multiplayer game or video chat
  • You’re targeting older browsers or HTTP/3 exclusively

In those cases, WebSockets are a better fit.

‍{{cool-component}}‍

Conclusion

SSE is one of those tools that quietly powers the real-time web. If you only need your server to send updates to the frontend, it’s cleaner, simpler, and more reliable than alternatives like polling or WebSockets.

Give it a try. You’ll be surprised how much you can do with just one open stream.

Published on:
July 28, 2025

Related Glossary

See All Terms
This is some text inside of a div block.