Server-Sent Events (SSE) is a web technology that enables servers to push updates to clients over a single HTTP connection. Unlike WebSocket which is bidirectional, SSE is one-way: server to client only. It's perfect for scenarios where you need real-time updates but the client doesn't need to send data back.
Server-Sent Events (SSE)
A standard for pushing real-time updates from server to client over HTTP.
SSE vs WebSocket vs Polling
| Feature | SSE | WebSocket | Polling |
|---|---|---|---|
| Direction | Server → Client | Bidirectional | Client → Server |
| Protocol | HTTP | WebSocket | HTTP |
| Complexity | Simple | More complex | Simple |
| Auto-reconnect | Built-in | Manual | N/A |
| Binary data | No (text only) | Yes | Yes |
| Browser support | All modern | All modern | All |
When to Use SSE
Live feeds: News updates, social media timelines, activity streams. Data flows in one direction - from server to user.
Notifications: Real-time alerts, system status updates, progress indicators. User doesn't send anything back, just receives.
Live dashboards: Metrics, analytics, monitoring displays. Data updates automatically without user interaction.
Progress tracking: File upload progress, long-running job status, deployment logs. Server pushes status updates.
AI/LLM streaming: ChatGPT-style responses that stream token by token. SSE is perfect for this - server pushes text chunks, client displays incrementally.
When NOT to Use SSE
Bidirectional communication: Chat apps, multiplayer games - if client needs to send messages frequently, use WebSocket.
Binary data: SSE only supports text. For images, files, or binary protocols, use WebSocket or HTTP.
Many concurrent connections: Each SSE connection holds a TCP connection open. At scale (millions of connections), this can strain servers.
How SSE Works
1. Client opens connection:
Client makes GET request with Accept: text/event-stream. Connection stays open.
2. Server sends events: Server writes events to the response stream. Each event has optional id, event type, and data.
3. Auto-reconnect:
If connection drops, browser automatically reconnects. Sends Last-Event-ID header so server can resume.
4. Connection stays open: Unlike regular HTTP, the response doesn't end. Server can keep pushing events indefinitely.
Event Format
SSE events are plain text with specific format:
data:The message content (required)event:Event type/name (optional)id:Event ID for resuming (optional)retry:Reconnection time in ms (optional)- Events separated by blank lines
EventSource API
The browser provides EventSource for consuming SSE:
Connection:
new EventSource(url)- Opens connectionclose()- Closes connection
Events:
onopen- Connection establishedonmessage- Default message receivedonerror- Error or connection lostaddEventListener(type, handler)- Named event types
Properties:
readyState- 0 (connecting), 1 (open), 2 (closed)url- Connection URL
Connection Management
Automatic reconnection:
SSE reconnects automatically after disconnection. Default is 3 seconds, configurable via retry: field.
Resumption:
Send event IDs. On reconnect, browser sends Last-Event-ID header. Server can resume from where it left off.
Heartbeats:
Send periodic empty comments (: heartbeat) to keep connection alive through proxies and load balancers.
SSE vs Polling Trade-offs
Why SSE beats polling:
- No repeated connection overhead
- Instant updates (no polling interval delay)
- Less server load (no wasted requests)
- Simpler client code
When polling might be better:
- Very infrequent updates (every 5+ minutes)
- Need to work through aggressive caching proxies
- Server can't hold connections open
Best Practices
Send event IDs: Always include IDs for reliable resumption. Without IDs, clients miss events during reconnection.
Use event types:
Don't put everything in the default message event. Use named types: event: notification, event: update.
Implement heartbeats: Some proxies close idle connections. Send periodic comments to keep the connection alive.
Handle reconnection gracefully: Client will reconnect automatically, but your app state might be stale. Fetch current state on reconnect.
Set appropriate retry interval:
Default 3 seconds might be too aggressive. Set retry: 10000 for less critical updates.
Common Mistakes
1. No event IDs: Connection drops, reconnects, client misses events. Always send IDs for resumable streams.
2. Forgetting CORS: SSE is still HTTP. If server and client are on different origins, you need proper CORS headers.
3. No heartbeats: Connection sits idle, proxy kills it, client doesn't know until it's too late. Send periodic heartbeats.
4. Blocking the event loop: On server, if event stream blocks, no other requests are processed. Use non-blocking I/O.
Code Examples
SSE Server and Client
// Server (Node.js/Express)
app.get('/events', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
// Send initial connection event
res.write('event: connected\ndata: {}\n\n');
let eventId = 0;
// Send updates every 5 seconds
const interval = setInterval(() => {
eventId++;
res.write(`id: ${eventId}\n`);
res.write('event: update\n');
res.write(`data: ${JSON.stringify({ time: new Date() })}\n\n`);
}, 5000);
// Heartbeat every 30 seconds
const heartbeat = setInterval(() => {
res.write(': heartbeat\n\n');
}, 30000);
// Cleanup on disconnect
req.on('close', () => {
clearInterval(interval);
clearInterval(heartbeat);
});
});
// Client (Browser)
const events = new EventSource('/events');
events.onopen = () => {
console.log('Connected to event stream');
};
events.addEventListener('update', (e) => {
const data = JSON.parse(e.data);
console.log('Update:', data);
updateUI(data);
});
events.addEventListener('connected', () => {
console.log('Stream established');
});
events.onerror = (e) => {
if (events.readyState === EventSource.CLOSED) {
console.log('Connection closed');
} else {
console.log('Error, will reconnect...');
}
};Related Terms
API Testing
The process of verifying that APIs work correctly, securely, and perform well.
API Versioning
Strategies for managing breaking changes in APIs while maintaining backward compatibility.
HTTP Status Codes
Standard response codes that indicate the result of an HTTP request.
API Mocking
Simulating API behavior to enable development and testing without real backend services.