Server-Sent Events (SSE) offer a deceptively simple way to push real-time updates over HTTP, but delivering them through a Content Delivery Network introduces a fundamental conflict: CDNs are built to buffer and cache, while SSE requires an unbuffered, long-lived stream. Without explicit edge configuration, your live scores, stock tickers, or AI token streams will stall, batch, or disconnect. Understanding how to tune edge buffering is critical for reliable streaming delivery.
Unlike WebSockets, SSE operates over plain HTTP with the text/event-stream content type. This makes it inherently compatible with CDN infrastructure, HTTP/2 multiplexing, and HTTP/3 over QUIC — if you configure the edge correctly. This guide explains how SSE works through a CDN, why default proxy behavior breaks it, and the exact headers and settings to achieve low-latency streaming at scale.
How Server-Sent Events Work Over HTTP
SSE is a W3C standard where the client opens a single GET request and the server holds the response open indefinitely, sending discrete events as text lines. Each event is delimited by double newlines and can include id:, event:, data:, and retry: fields.
// Client: native browser API with auto-reconnect const source = new EventSource('/api/live-prices'); source.addEventListener('price-update', (e) => { const data = JSON.parse(e.data); render(data); }); source.onerror = () => { // Browser automatically retries with Last-Event-ID header console.log('Reconnecting...', source.readyState); };
On the wire, the stream looks like this:
HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache, no-transform Connection: keep-alive X-Accel-Buffering: no id: 1042 event: price-update data: {"symbol":"AAPL","price":214.32} id: 1043 data: {"symbol":"MSFT","price":512.10} : keep-alive comment
Key advantages for CDN delivery are significant. SSE reuses standard HTTP semantics, so it traverses firewalls, benefits from TLS, and works without protocol upgrades. The browser handles reconnection and de-duplication via Last-Event-ID, which is essential when edge connections reset. For a refresher on how requests traverse the edge, see how CDN works.
Why CDNs Break SSE by Default
A CDN edge node is a reverse proxy optimized for throughput, not latency. Three default behaviors conflict with streaming:
| Metric | Edge Processing | Origin Processing | Improvement |
|---|---|---|---|
| Latency (p50) | 8–15 ms | 120–300 ms | 15–20x faster |
| Cache Hit Ratio | 85–95% | N/A | Reduces origin load |
| TLS Handshake | 1–5 ms (nearby PoP) | 50–150 ms | 10–30x faster |
| Bandwidth Cost | $0.01–0.04/GB | $0.05–0.12/GB | 3–5x cheaper |
| Availability | 99.99% (multi-PoP) | 99.9% (single DC) | 10x fewer outages |
1. Response Buffering
NGINX, Varnish, CloudFront, Fastly, and Cloudflare all buffer origin responses to disk or memory before forwarding to the client. This coalesces small chunks into larger TCP segments and reduces origin load. For SSE, buffering means a 30-byte event sits in a 4KB-32KB buffer for seconds or minutes until the buffer flushes, creating artificial latency.
2. Connection Timeouts and Idle Limits
Edge proxies enforce idle timeouts (typically 60-120 seconds) and maximum connection durations. An SSE stream that sends an event every 45 seconds may be terminated as idle if no keep-alive comments are sent. Understanding CDN fundamentals helps clarify why these limits exist for origin protection.
3. HTTP/2 and HTTP/3 Multiplexing Interactions
Modern CDNs terminate HTTP/2 and HTTP/3 at the edge. While multiplexing allows SSE to share a connection with other assets, improper flow-control window settings can stall the stream if the client is slow. Conversely, HTTP/2 server push is deprecated — SSE is the correct replacement for push-style updates.
Edge Buffering Architectures
There are two viable architectures for SSE at the edge. The choice depends on scale and whether you need fan-out.
Transparent Proxy (Recommended): The edge maintains a 1:1 connection to origin for each client. It disables all buffering and forwards chunks instantly. This is simple, preserves Last-Event-ID semantics, and works on all major CDNs. The cost is origin connection count, which scales linearly with clients.
Edge Fan-Out / Pub-Sub: Advanced edge compute platforms (Cloudflare Workers, Fastly Compute@Edge, AWS Lambda@Edge) subscribe once to origin and replicate the stream to thousands of edge clients via an internal pub/sub. This dramatically reduces origin load but requires custom logic to handle replay, ordering, and per-client Last-Event-ID tracking. Use this only when you exceed ~10k concurrent streams per region.
Configuring the CDN for SSE
Correct headers must be set at the origin and respected at the edge. No cache layer should store the stream.
# Origin response headers — critical for all CDNs Content-Type: text/event-stream Cache-Control: no-cache, no-store, must-revalidate Pragma: no-cache X-Accel-Buffering: no # Disables buffering in NGINX, Fastly, Cloudflare Content-Encoding: none # Disable gzip/brotli for streams
NGINX / Edge Proxy
location /api/stream {
proxy_pass http://origin;
proxy_set_header Connection '';
proxy_http_version 1.1;
proxy_buffering off;
proxy_cache off;
chunked_transfer_encoding on;
tcp_nodelay on;
keepalive_timeout 360s;
proxy_read_timeout 360s;
}
Cloudflare / Fastly / CloudFront
- Cloudflare: Set Cache Rule → Bypass cache when URL contains
/stream. Workers automatically disable buffering when you return aReadableStream. Ensure no Automatic Compression fortext/event-stream. - Fastly VCL:
set beresp.do_stream = true; set beresp.do_gzip = false; set beresp.ttl = 0s; - CloudFront: Set origin Cache Policy to
CachingDisabled, Origin Request Policy to forwardLast-Event-ID, and disable compression. Increase origin response timeout to 60s+.
Also forward the Last-Event-ID header explicitly. Many CDNs strip unknown headers by default. Without it, reconnections lose continuity and clients miss events. For DNS-level resilience of these long-lived connections, review CDN DNS resolution.
SSE vs Alternatives at the Edge
| Feature | SSE | WebSockets | Long Polling |
|---|---|---|---|
| Direction | Server → Client only | Bidirectional | Server → Client (emulated) |
| Protocol | HTTP (text/event-stream) | ws/wss (Upgrade) | HTTP |
| CDN Friendliness | High — standard HTTP | Low — stateful upgrade | Medium — high overhead |
| Reconnection | Automatic + Last-Event-ID | Manual | Manual |
| Buffering Risk | Must disable buffering | No buffering (tunneled) | Buffering less critical |
| HTTP/2/3 Support | Native multiplexing | Requires extensions | Works but inefficient |
| Best For | Feeds, notifications, LLM streams | Chat, gaming, collaboration | Legacy fallback |
For one-way updates, SSE is more efficient than WebSockets at the edge because it avoids connection upgrade handshakes and works seamlessly with edge computing runtimes. If you need bidirectional communication, consider WebSockets or WebTransport instead.
Performance and Reliability Optimization
Keep-Alive and Heartbeats
Send a comment line every 15-25 seconds to prevent idle timeouts and keep TCP windows open:
# Server (Node.js example) setInterval(() => { res.write(': heartbeat\n\n'); // Ignored by EventSource }, 20000);
Set retry: 3000 in the stream to control client backoff. Exponential backoff on the client prevents thundering herds when origin restarts.
Compression and TLS
Never compress text/event-stream. Gzip buffers until it has enough data to compress efficiently, reintroducing latency. Use Content-Encoding: none and rely on TLS 1.3 for efficiency. HTTP/3 over QUIC further improves lossy-network performance for streams.
Monitoring
Track edge metrics: origin_response_time, concurrent SSE connections per PoP, 502/504 rates, and client reconnection frequency. A spike in Last-Event-ID replays indicates edge instability. Set alerts if p95 time-to-first-byte for /stream exceeds 500ms.
Graceful Shutdown
When deploying origin, send retry: 10000 followed by closing the connection. Clients will reconnect to another edge PoP via Anycast. Avoid sending Connection: close mid-stream without a retry directive.
When to Use SSE at the Edge
SSE excels for: live dashboards, sports and financial tickers, activity feeds, build logs, and LLM token streaming where the server pushes tokens as they generate. It is ideal when clients are browsers or mobile apps that benefit from automatic reconnection.
Avoid SSE when you need client-to-server streaming, binary payloads, or ultra-low latency under 100ms with bidirectional control — WebSockets or WebRTC are better. Also avoid caching SSE; if you need to cache near-real-time data, use short-TTL JSON polling instead.
Done right, SSE through a CDN gives you global real-time delivery without managing WebSocket infrastructure. The key is treating the edge as a low-latency conduit: disable buffering, preserve headers, heartbeat frequently, and let HTTP do what it was designed to do.