# Secure server-sent events and reconnect handling

> Server-Sent Events (SSE) provide a robust mechanism for unidirectional server-to-client streaming over HTTP. However, the protocol's automatic reconnection behavior introduces subtle authorization and state management challenges. This article explores how browsers handle SSE disconnects, how the Last-Event-ID header functions during recovery, and how to securely design SSE endpoints to prevent unauthorized access or state corruption during network interruptions.

Official source: https://warhoundai.com/blog/sse-authorization-disconnects/
Published: 2026-11-12
Author: WarHoundAI

## The SSE Connection Lifecycle

Server-Sent Events (SSE) establish a persistent HTTP connection where the server pushes data to the client using the `text/event-stream` MIME type. Unlike WebSockets, SSE is unidirectional (server-to-client) and leverages standard HTTP semantics. The `EventSource` interface manages this connection, automatically attempting to reconnect if the connection drops.

When an `EventSource` object is created, its `readyState` is initialized to `CONNECTING` (0). Once the connection is established and the server responds with the correct content type, the state transitions to `OPEN` (1), and the client begins parsing the stream. If a network error occurs or the connection drops, the browser automatically queues a task to reestablish the connection after a delay (the reconnection time), transitioning back to `CONNECTING` and firing an `error` event.

- [HTML Standard: Server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html)

## Automatic Reconnection Behavior

The automatic reconnection feature of SSE is highly resilient. If the connection fails, the user agent waits for a specified delay before retrying. This delay can be controlled by the server using the `retry` field in the event stream. If the previous attempt failed, browsers may introduce exponential backoff to avoid overloading the server.

Crucially, the client will stop attempting to reconnect only under specific conditions: if the server responds with an HTTP 204 No Content, if the response status is not 200, or if the `Content-Type` is not `text/event-stream`. If the connection is explicitly closed by the client using the `close()` method, the `readyState` becomes `CLOSED` (2), and no further reconnection attempts are made.

## State Recovery with Last-Event-ID

To ensure no events are lost during a disconnect, SSE utilizes the `id` field. When the server includes an `id` field in an event block, the client updates its internal last event ID string. If the connection drops and the client attempts to reconnect, it includes this value in the `Last-Event-ID` HTTP request header.

The `Last-Event-ID` header allows the server to determine exactly where the client left off and resume the stream from that point. The server must track event IDs and retain a buffer of recent events to serve reconnecting clients. If an event contains an empty `id` field, the client resets its last event ID string, meaning no `Last-Event-ID` header will be sent on subsequent reconnects.

## Authorization Challenges During Reconnects

Because SSE relies on standard HTTP requests, initial authorization is typically handled via cookies or query parameters. The `EventSource` constructor accepts a `withCredentials` option to include credentials in cross-origin requests. However, authorization tokens (like JWTs) passed via query parameters pose security risks if logged, and cookies must be carefully scoped.

The primary authorization challenge arises during reconnects. If a user's session expires or their permissions change while the SSE connection is open, the server must enforce these changes. Since the browser handles reconnections automatically, the server must re-validate the user's authorization state on every reconnect request. If the user is no longer authorized, the server must reject the reconnect (e.g., with a 401 or 403 status), which instructs the browser to fail the connection and stop retrying.

## Implementation Tradeoffs and Failure Modes

Implementing robust SSE requires managing state on the server. Buffering events for reconnecting clients consumes memory. If the server restarts or the buffer is cleared, a client connecting with a valid `Last-Event-ID` might request an event that no longer exists. The server must handle this gracefully, perhaps by sending a full state snapshot instead of a delta.

Another common failure mode involves proxy servers. Legacy proxies may drop long-lived HTTP connections after a timeout. To prevent this, servers can send periodic "keep-alive" comments (lines starting with a colon). Additionally, HTTP chunking implemented by intermediary layers unaware of SSE timing requirements can delay event delivery. Disabling chunking for event streams may be necessary in some environments.

## Test Matrix and Worked Example

When testing SSE endpoints, it is critical to verify reconnection logic and authorization enforcement. Consider a hypothetical synthetic fixture where an SSE endpoint `/api/stream` requires a valid session cookie. A safe test matrix should verify the following expected outcomes:

1. **Initial Connection**: Valid cookie -> HTTP 200, `text/event-stream`. 2. **Reconnect with Last-Event-ID**: Valid cookie, `Last-Event-ID: 42` -> HTTP 200, stream resumes from event 43. 3. **Expired Session Reconnect**: Invalid cookie -> HTTP 401, browser stops reconnecting. 4. **Missing Event ID**: Valid cookie, `Last-Event-ID: 9999` (event purged) -> HTTP 200, server sends full state snapshot or error event indicating state loss. 5. **Incorrect Content-Type**: Valid cookie -> HTTP 200, `application/json` -> browser fails connection.

- Valid session: HTTP 200, correct MIME type.
- Invalid session on reconnect: HTTP 401/403, halting reconnects.
- Purged Last-Event-ID: Graceful state recovery.

## Sources

HTML Standard: Server-sent events - https://html.spec.whatwg.org/multipage/server-sent-events.html

Drafted with Gemini and checked for source links, advisory identifiers, structure and unsupported product claims. Examples are synthetic and are not scan results.

Drafted with Gemini and checked for source links, advisory identifiers, structure and unsupported product claims. Examples are synthetic and are not scan results.

- [Primary reference: html.spec.whatwg.org](https://html.spec.whatwg.org/multipage/server-sent-events.html)
