HTML Server-Sent Events

Beginner
⏱️ 14 min read
📚 Updated: Jul 2026
🎯 5 Examples + 3 Try It
EventSource

Introduction

Server-Sent Events (SSE) let a server push updates to a web page over a single, long-lived HTTP connection. This is ideal for live news tickers, sports scores, stock prices, notifications, and dashboards—anywhere the browser needs fresh data without constantly asking the server “anything new yet?”

SSE is simpler than WebSockets when you only need server → client communication. This tutorial covers the wire format, a Node.js server, the browser EventSource API, and minimal Try It demos you can run without setting up a backend.

What You’ll Learn

01

SSE format

data: lines.

02

Node server

event-stream.

03

EventSource

Client API.

04

Reconnect

Auto retry.

05

Event types

Named events.

06

Security

HTTPS & CORS.

What Are Server-Sent Events?

Server-Sent Events (SSE) is a web technology where the server sends updates to the client as they happen. The client opens one HTTP connection and keeps it open; the server writes new messages whenever data changes.

Unlike polling (repeated fetch calls every few seconds), SSE is efficient: one connection, many updates. Unlike WebSockets, SSE is text-based, works through most HTTP proxies, and only flows in one direction—which is exactly what many live feeds need.

💡
Beginner Tip

Think of SSE like a radio broadcast: the server transmits, your page listens. You do not send messages back through the same SSE connection (use fetch or WebSockets for client → server).

How Do Server-Sent Events Work?

SSE works over ordinary HTTP. The server responds with Content-Type: text/event-stream and keeps the connection open. Each message is plain text with a simple structure:

  • data: — the payload (one or more lines; required for default messages).
  • event: — optional event name (defaults to message).
  • id: — optional id so the client can resume after reconnect.
  • retry: — optional reconnection delay in milliseconds.
  • Blank line — two newline characters (\n\n) end each event.

Example on the wire:

SSE wire format
data: Stock price is 142.50

event: alert
data: Trading halted

The browser’s EventSource parses this stream and fires JavaScript events you can handle.

Setting Up Server-Sent Events

On the server, set headers for a streaming response and write data: lines followed by a blank line. Flush output so the client receives events immediately.

Here is a minimal Node.js example that sends the current time every second:

js
const http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  var timer = setInterval(function () {
    res.write('data: ' + new Date().toLocaleTimeString() + '\n\n');
  }, 1000);

  req.on('close', function () {
    clearInterval(timer);
  });
}).listen(8080, function () {
  console.log('SSE server on http://localhost:8080');
});

Run with node sse-server.js, then point your page’s EventSource at http://localhost:8080.

Receiving Events on the Client Side

Use the EventSource API in the browser. The default message event fires for plain data: lines:

js
var eventSource = new EventSource('http://localhost:8080');

eventSource.onmessage = function (event) {
  console.log('New message:', event.data);
};

eventSource.onopen = function () {
  console.log('SSE connection opened');
};

Call eventSource.close() when the page no longer needs updates (for example when navigating away).

Reconnecting Automatically

A key feature of SSE is automatic reconnection. If the network drops, EventSource retries after a short delay. Listen for errors to show status in your UI:

js
eventSource.onerror = function (event) {
  console.error('EventSource failed — browser will retry');
  // readyState: 0 = connecting, 1 = open, 2 = closed
  if (eventSource.readyState === EventSource.CLOSED) {
    console.log('Connection closed permanently');
  }
};

The server can suggest a retry interval with retry: 3000\n\n (wait 3 seconds before reconnecting).

Handling Different Event Types

Send a named event from the server with the event: field. On the client, use addEventListener instead of onmessage:

js
// Server
res.write('event: customEvent\n');
res.write('data: This is a custom event\n\n');
js
// Client
eventSource.addEventListener('customEvent', function (event) {
  console.log('Custom event:', event.data);
});

Default unnamed events still trigger onmessage. Named events do not.

Security Considerations

SSE rides on HTTP, so apply the same security practices as any web API:

  • Use HTTPS in production so stream data cannot be read or modified on the network.
  • Validate and escape data before inserting into HTML—treat event.data like any untrusted string (use textContent, not innerHTML).
  • Authenticate streams when content is private (cookies or tokens on the same origin).
  • Configure CORS if the EventSource URL is on a different domain than your page.
  • Rate-limit connections on the server to prevent abuse of open streams.

⚡ Quick Reference

TaskCode / format
Server content typetext/event-stream
Send one messageres.write('data: hello\\n\\n')
Named eventevent: name\\ndata: payload\\n\\n
Open stream (client)new EventSource(url)
Default messageses.onmessage = fn
Named messageses.addEventListener('name', fn)
Close streames.close()
Server
data: ...\n\n

Wire format

Client
EventSource

Browser API

Direction
server → client

One-way

Retry
automatic

Built-in

Examples Gallery

Five short examples from server setup to a full clock page. Try It Yourself demos use plain HTML with no CSS and simulate the stream so you can practice client handlers without running Node.js.

📚 Getting Started

Server stream and client listener.

Example 1 — Minimal SSE Server (Node.js)

Send the time every second with correct headers.

js
const http = require('http');

http.createServer(function (req, res) {
  res.writeHead(200, {
    'Content-Type': 'text/event-stream',
    'Cache-Control': 'no-cache',
    'Connection': 'keep-alive'
  });

  setInterval(function () {
    res.write('data: ' + new Date().toLocaleTimeString() + '\n\n');
  }, 1000);
}).listen(8080);
Try It Yourself

How It Works

Each res.write pushes one SSE event. The double newline ends the event so the browser can parse it.

Example 2 — Client EventSource

Connect and log each message.

js
var eventSource = new EventSource('http://localhost:8080');

eventSource.onmessage = function (event) {
  console.log('New message:', event.data);
};
Try It Yourself

How It Works

event.data is always a string. Parse JSON yourself if the server sends structured data.

📈 Practical Patterns

Errors, custom types, and a complete HTML demo.

Example 3 — Handle Connection Errors

Show retry status when the stream fails.

js
eventSource.onerror = function () {
  console.error('Connection error. Retrying...');
};
Try It Yourself

How It Works

The browser reconnects automatically unless you call close() or the server returns a fatal error.

Example 4 — Custom Event Type

Listen for a named event from the server.

js
// Server sends:
// event: news
// data: Breaking story
// (blank line)

eventSource.addEventListener('news', function (event) {
  console.log('News:', event.data);
});
Try It Yourself

How It Works

Use different event names to route updates to different UI components (news feed vs alerts vs chat).

Example 5 — Example (Full Page)

Real-time clock from the reference tutorial—plain HTML, no CSS:

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Server-Sent Events Example</title>
</head>
<body>
  <h1>Real-Time Clock</h1>
  <div id="clock"></div>

  <script>
    var eventSource = new EventSource('http://localhost:8080');

    eventSource.onmessage = function (event) {
      document.getElementById('clock').textContent = event.data;
    };

    eventSource.onerror = function () {
      console.error('Connection error. Retrying...');
    };
  </script>
</body>
</html>
Try It Yourself

How It Works

Run the Node server from Example 1, open this HTML page, and the clock updates every second from the stream.

🚀 Common Use Cases

  • Live news and sports tickers — push headlines as they publish.
  • Stock and crypto prices — stream price changes to dashboards.
  • Notifications — alert users without polling every few seconds.
  • Progress updates — long server jobs report status (% complete).
  • Social feeds — new posts appear at the top in real time.
  • Monitoring dashboards — server metrics streamed to ops teams.

🧠 How SSE Works (Step by Step)

1

Client opens stream

new EventSource(url) starts an HTTP GET.

Connect
2

Server sets headers

text/event-stream keeps connection alive.

Stream
3

Server writes events

data: ...\n\n for each update.

Push
4

Browser fires events

onmessage or addEventListener.

Handle
=

Live UI

Page updates without refresh or polling.

📝 Notes

  • SSE is one-way—only server → client on that connection.
  • Internet Explorer does not support EventSource; all modern browsers do.
  • Some older proxies buffer responses; SSE needs streaming-friendly infrastructure.
  • Browser limit: roughly six SSE connections per domain (similar to HTTP/1.1).
  • For bidirectional chat or games, consider WebSockets instead.
  • Try It demos simulate streams so you can learn handlers without a backend.

Universal Browser Support

EventSource is supported in Chrome, Firefox, Safari, Edge, and other modern browsers. It is not available in Internet Explorer. For legacy IE, use polling or a polyfill library.

Baseline · Since HTML

Server-Sent Events API

EventSource is supported in Chrome, Firefox, Safari, Edge, and other modern browsers. It is not available in Internet Explorer. For legacy IE, use polling or a polyfill library.

94% Modern browser support
Google Chrome All versions · Desktop & Mobile
Full support
Mozilla Firefox All versions · Desktop & Mobile
Full support
Apple Safari All versions · macOS & iOS
Full support
Microsoft Edge All versions · Chromium & Legacy
Full support
Internet Explorer IE 6+ · Legacy environments
Full support
Opera All modern versions
Full support
Server-Sent Events API Excellent

Bottom line: Safe for modern web apps. Use HTTPS and test behind your production CDN/proxy.

💡 Best Practices

✅ Do

  • Use SSE when only the server needs to push data
  • Set text/event-stream and disable caching on the server
  • Close EventSource when leaving the page
  • Parse JSON safely from event.data
  • Use named events for different update types
  • Serve streams over HTTPS in production

❌ Don’t

  • Use SSE for two-way real-time chat (use WebSockets)
  • Insert raw event.data into HTML with innerHTML
  • Forget to clear server timers when clients disconnect
  • Send huge payloads on every tick without throttling
  • Assume IE support without a fallback
  • Open dozens of EventSource connections per page

Conclusion

Server-Sent Events offer a straightforward way to implement real-time updates in web applications. The server writes data: lines over HTTP; the browser listens with EventSource and updates the UI in onmessage.

You get automatic reconnection, simple text format, and no WebSocket infrastructure. Try the demos, run the Node.js server locally, and build responsive apps that keep users updated without constant polling.

Key Takeaways

Knowledge Unlocked

Five things to remember about SSE

Use these points when choosing server push for your app.

5
Core concepts
📄 02

data:\n\n

Wire format.

Format
💻 03

EventSource

Client API.

Browser
🔄 04

Auto retry

Reconnects.

Reliable
🔒 05

HTTPS

Production.

Security

❓ Frequently Asked Questions

SSE is a web standard for pushing text updates from a server to a browser over a single long-lived HTTP connection. The client uses the EventSource API to listen; the server sends messages in a simple text format (data: ... followed by a blank line).
SSE is one-way (server to client only) and runs over ordinary HTTP. WebSockets are two-way and use a different protocol. Choose SSE for live feeds, notifications, and dashboards where only the server sends updates.
EventSource is the browser interface for SSE. You call new EventSource(url) and listen with onmessage or addEventListener. The browser handles reconnection automatically if the connection drops.
Set Content-Type to text/event-stream, plus Cache-Control: no-cache and Connection: keep-alive. Each message is text lines ending with a blank line, for example: data: Hello followed by two newline characters.
Yes. Put JSON in the data line as a string: data: {"price":42}. On the client, parse it with JSON.parse(event.data) inside your onmessage handler.
For production, yes—use HTTPS to protect data in transit. Local development on http://localhost is fine for learning. Also configure CORS if the EventSource URL is on a different origin.
Did you know?

SSE uses the same HTTP port as normal web pages (80/443), so it often works through corporate firewalls that block WebSockets. That is one reason teams pick SSE for simple live dashboards before adopting full duplex protocols.

Practice SSE client handlers

Try simulated streams in the editor—plain HTML, no backend required. Then run the Node.js server for a real connection.

Open Try It editor →

About the author

Mari Selvan M P
Mari Selvan M P 🔗

Developer, cloud engineer, and technical writer

  • Experience 12 years building web and cloud systems
  • Focus Full Stack Development, AWS, and Developer Education

I write practical tutorials so students and working developers can learn by doing—from databases and APIs to deployment on AWS.

5 people found this page helpful