SSE Protocol Deep Dive

The SSE Protocol

Server-Sent Events uses a simple text-based protocol over HTTP. Understanding the protocol helps you debug SSE connections and understand server behavior.

Key Protocol Features
  • • Uses standard HTTP GET request
  • • Content-Type: text/event-stream
  • • Connection kept open indefinitely
  • • Events are plain text, one per "block"
  • • Blank line separates events

HTTP Request & Response

Client Request
GET /events HTTP/1.1
Host: api.example.com
Accept: text/event-stream
Cache-Control: no-cache
Last-Event-ID: 42
Authorization: Bearer token...
Accept header tells server we want SSE. Last-Event-ID is sent for reconnection (if resuming).
Server Response
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
id: 43
event: message
data: {"hello": "world"}
(blank line)
id: 44
data: {"more": "data"}
(blank line)
...
Content-Type: text/event-stream identifies SSE response. Events flow continuously until disconnected.

Event Format

Each event is a block of field: value lines, terminated by a blank line:

id:value← Sets event ID for reconnection
event:value← Sets event type (default: message)
data:value← Event payload (can repeat)
retry:value← Reconnect delay in ms
← Blank line dispatches event
Minimal Event
data: Hello

Just data and a blank line. Type defaults to "message", no ID.

Full Event
id: 123
event: notification
data: {"title": "Alert"}
retry: 5000

Complete event with all fields specified.

Field Reference

idEvent Identifier

Sets the event's unique identifier. After receiving an event with an ID, the client stores it as the "last event ID". On reconnection, the client sends this ID in the Last-Event-ID header.

id: user-123-msg-456
data: New message arrived

Note: IDs can be any string, not just numbers. Common patterns: UUIDs, timestamps, sequential numbers.

eventEvent Type

Specifies the event type name. Clients can listen for specific event types. If omitted, defaults to message.

event: update
data: {"status": "online"}
event: error
data: Connection lost

Common types: message, update, error, ping, notification

dataEvent Payload

Contains the actual event data. Can appear multiple times for multiline content. Each line is joined with a newline character.

Multiline in protocol:
data: {
data: "name": "John",
data: "age": 30
data: }
Received as:
{ "name": "John", "age": 30 }
retryReconnection Delay

Tells the client how long to wait (in milliseconds) before attempting to reconnect after a disconnect.

retry: 5000
data: Server will be down for maintenance

Default: Browser default is typically 3 seconds. Server can override per-event or send it once at connection start.

Comments & Keep-Alive

Lines starting with a colon (:) are comments and are ignored by the client:

: this is a comment
: ping
: keepalive 1706123456
Keep-Alive Mechanism

Servers often send periodic comments to prevent connection timeouts. Proxies and load balancers may close idle connections, so sending a comment every 15-30 seconds keeps the connection alive without triggering client events.

Connection Lifecycle

1

Connect

Client sends GET request to SSE endpoint. Server responds with 200 OK and keeps connection open.

2

Stream

Server sends events as they occur. Client receives and processes each event. Connection stays open indefinitely.

3

Disconnect

Connection can be closed by: client (user clicks Disconnect), server (sends empty response), or network (timeout, error).

4

Reconnect (Optional)

Client can reconnect with Last-Event-ID header to resume from where it left off. Server must support this feature.

Error Handling

SSE connections can fail for various reasons:

ErrorCauseSolution
Network ErrorNo internet, DNS failureCheck network connection
HTTP 401/403Invalid/expired credentialsUpdate authentication settings
HTTP 404Endpoint not foundVerify URL is correct
TimeoutNo keep-alive, proxy timeoutServer should send periodic comments
Server ClosedServer intentionally ended streamReconnect if needed

Best Practices

Use Event IDs

Always include event IDs if you support reconnection. This allows clients to resume without missing events.

Send Keep-Alives

Send a comment (: ping) every 15-30 seconds to prevent proxy timeouts.

Use JSON for Data

While SSE supports any text, JSON is widely used and allows structured data with automatic parsing.

Use Event Types

Differentiate events with types (update, error, notification) instead of putting the type in the data payload.