Skip to content

Server-Sent Events

This lesson explains Server-Sent Events with clear examples, use cases, and best practices so you can use HTML5 APIs confidently in real web applications.

HTML5 Server-Sent Events API Overview

The HTML5 Server-Sent Events API allows a server to push real-time updates to the browser over a single long-lived HTTP connection. It uses the EventSource interface on the client and a text/event-stream response from the server.

Server-Sent Events are commonly used for live notifications, dashboards, stock price updates, sports scores, news feeds, progress updates, chat message notifications, activity streams, monitoring tools, and any feature that needs server-to-browser updates without full WebSocket complexity.

Feature Description
API Name Server-Sent Events API
Main Client Object EventSource
Data Direction Server to browser only
Content Type text/event-stream
Connection Type Long-lived HTTP connection
Best Use Live updates, notifications, progress, feeds, dashboards.

Basic EventSource Client Example

// HTML5 Server-Sent Events API

const events =
  new EventSource("/events");

events.onmessage = (event) => {
  console.log(
    "Message from server:",
    event.data
  );
};

events.onerror = () => {
  console.log(
    "SSE connection error or reconnecting."
  );
};

The browser opens a persistent connection to /events. Every message sent by the server is received through the message event.

Basic Server Response Format

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

data: Hello from server

data: Another update

Each SSE message uses lines such as data:, event:, id:, and retry:. A blank line ends one event message.

Node.js SSE Server Example

// Express-style Server-Sent Events endpoint

app.get("/events", (request, response) => {
  response.setHeader(
    "Content-Type",
    "text/event-stream"
  );

  response.setHeader(
    "Cache-Control",
    "no-cache"
  );

  response.setHeader(
    "Connection",
    "keep-alive"
  );

  const timer =
    setInterval(() => {
      const payload =
        JSON.stringify(
          {
            time: new Date().toISOString()
          }
        );

      response.write(
        "data: " + payload + "\n\n"
      );
    }, 1000);

  request.on("close", () => {
    clearInterval(timer);
    response.end();
  });
});

This example sends a JSON timestamp every second. The server keeps the connection open until the client disconnects.

How Server-Sent Events Work

Step Description Example Syntax
Create Connection Browser opens an SSE connection. new EventSource("/events")
Server Holds Response Server keeps HTTP response open. text/event-stream
Send Updates Server writes event-stream messages. data: message\n\n
Receive Updates Browser receives messages automatically. events.onmessage
Reconnect Browser reconnects if connection drops. retry: 5000
Close Connection Client can stop listening. events.close()

EventSource Events

Event When It Runs Common Use
open SSE connection opens successfully. Show connected status.
message Default server message is received. Read general updates.
error Connection error or reconnect attempt occurs. Show reconnecting or fallback state.
Custom Events Named server event is received. Route notifications, alerts, and progress events.

Named Events Example

event: notification
data: You have a new message

event: progress
data: 75
const events =
  new EventSource("/events");

events.addEventListener(
  "notification",
  (event) => {
    console.log(
      "Notification:",
      event.data
    );
  }
);

events.addEventListener(
  "progress",
  (event) => {
    console.log(
      "Progress:",
      event.data + "%"
    );
  }
);

Named events help separate different types of server updates, making client-side handling cleaner and easier to maintain.

Send and Receive JSON Data

event: order-update
data: {"orderId":123,"status":"shipped"}
events.addEventListener(
  "order-update",
  (event) => {
    const data =
      JSON.parse(event.data);

    console.log(
      data.orderId,
      data.status
    );
  }
);

Server-Sent Events send text data, so structured data should be serialized as JSON and parsed on the client.

Event Stream Fields

Field Description Example
data Message data sent to the browser. data: Hello
event Custom event name. event: notification
id Message ID used for reconnect recovery. id: 42
retry Reconnect delay in milliseconds. retry: 5000
Comment Lines starting with colon can keep connection alive. : heartbeat

Reconnect with Last Event ID

id: 101
event: notification
data: New alert

retry: 3000

When the SSE connection reconnects, the browser can send the last received event ID using the Last-Event-ID header. This helps the server resume missed messages when supported by the backend.

Close EventSource Connection

const events =
  new EventSource("/events");

document
  .querySelector("#stop")
  .addEventListener("click", () => {
    events.close();

    console.log(
      "SSE connection closed."
    );
  });

Use close() when updates are no longer needed, such as when users leave a dashboard or disable live updates.

EventSource Ready State

console.log(
  events.readyState
);
Value Constant Meaning
0 CONNECTING Connection is opening or reconnecting.
1 OPEN Connection is open and receiving events.
2 CLOSED Connection has been closed.

Send Cookies with EventSource

const events =
  new EventSource(
    "/events",
    {
      withCredentials: true
    }
  );

Use withCredentials when your SSE endpoint requires cookies or authentication credentials. Configure CORS carefully if the endpoint is cross-origin.

Keep Connection Alive with Heartbeats

: heartbeat

: another heartbeat

Some servers and proxies close idle connections. Sending comment lines as heartbeats can help keep long-lived SSE connections active.

File Processing Progress Example

const progressEvents =
  new EventSource("/upload-progress");

progressEvents.addEventListener(
  "progress",
  (event) => {
    const progress =
      Number(event.data);

    document.querySelector("#progress").style.width =
      progress + "%";

    document
      .querySelector("#progress")
      .setAttribute(
        "aria-valuenow",
        progress
      );

    if (progress === 100) {
      progressEvents.close();
    }
  }
);

SSE is useful for long-running backend jobs such as imports, exports, video processing, file scanning, and report generation.

Feature Detection

if ("EventSource" in window) {
  console.log("Server-Sent Events supported.");
} else {
  console.log("Use polling or WebSocket fallback.");
}

Always provide fallback behavior when real-time updates are important for users on unsupported browsers or restricted environments.

Server-Sent Events vs WebSocket

Feature Server-Sent Events WebSocket
Data Direction Server to browser only. Two-way communication.
Protocol HTTP. WebSocket protocol.
Automatic Reconnect Built in. Manual reconnect logic needed.
Best For Notifications, feeds, progress, live dashboards. Chat, multiplayer games, collaboration, trading apps.
Complexity Simpler. More flexible but more complex.

Common Server-Sent Events Use Cases

  • Live notification feeds.
  • Dashboard metrics.
  • News and activity streams.
  • File upload or processing progress.
  • Build and deployment logs.
  • Stock, crypto, and pricing updates.
  • Order status and delivery updates.
  • Monitoring and alerting tools.

Server-Sent Events Best Practices

  • Use text/event-stream as the response content type.
  • Disable response buffering when using proxies or reverse proxies.
  • Send heartbeat comments to keep idle connections alive.
  • Use event IDs when clients need reconnect recovery.
  • Close EventSource connections when updates are no longer needed.
  • Send small JSON messages instead of large payloads.
  • Use WebSockets when two-way real-time communication is required.
  • Handle authentication, CORS, and rate limits carefully.

Security and Privacy Considerations

  • Authenticate SSE endpoints when updates are user-specific.
  • Do not stream private data to unauthorized clients.
  • Use HTTPS for production event streams.
  • Validate user access before sending each event.
  • Be careful with cross-origin event streams and credentials.
  • Avoid sending secrets or sensitive tokens in event data.
  • Rate-limit high-frequency streams to protect server resources.
  • Clean up server timers and listeners when clients disconnect.

Common Server-Sent Events Mistakes

  • Using SSE for two-way communication when WebSocket is more appropriate.
  • Forgetting the blank line after each event message.
  • Sending the wrong Content-Type.
  • Not disabling proxy buffering for event streams.
  • Not cleaning up intervals after client disconnects.
  • Streaming sensitive data without authentication.
  • Keeping unnecessary connections open forever.
  • Sending large payloads too frequently.

Key Takeaways

  • Server-Sent Events send real-time updates from server to browser.
  • Use new EventSource("/events") on the client.
  • Use text/event-stream on the server.
  • SSE supports automatic reconnect and message IDs.
  • Use named events to organize different update types.
  • Use WebSockets when you need full two-way communication.

Pro Tip

Use Server-Sent Events when your app mostly needs server-to-client updates such as notifications, progress, logs, and dashboards. It is simpler than WebSockets, works over HTTP, and includes automatic reconnect behavior.