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 Mocking
Simulating API behavior to enable development and testing without real backend services.
Service Virtualization
An enterprise-grade technique for simulating the behavior of dependent services, APIs, and systems that are unavailable or costly to access.
CORS
Cross-Origin Resource Sharing - A security mechanism that controls how web pages can request resources from different domains.
Rate Limiting
A technique to control the number of API requests a client can make within a time window.