Home / Protocols / CDN Server-Sent Events
Protocols

CDN Server-Sent Events: Streaming Data Delivery and Edge Buffering

By Emma Rodriguez, Cloud Infrastructure Specialist•September 6, 2026•9 min read

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:

Performance Comparison: Edge vs Origin Processing
Metric Edge Processing Origin Processing Improvement
Latency (p50)8–15 ms120–300 ms15–20x faster
Cache Hit Ratio85–95%N/AReduces origin load
TLS Handshake1–5 ms (nearby PoP)50–150 ms10–30x faster
Bandwidth Cost$0.01–0.04/GB$0.05–0.12/GB3–5x cheaper
Availability99.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.

Core principle: For SSE, the CDN must act as a transparent byte-for-byte proxy, not as a cache or aggregator. Every byte from origin should be flushed to the client immediately.

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.

SSE Through CDN: Transparent Proxy vs Buffered Path Diagram showing origin streaming to CDN edge with buffering disabled, forwarding instantly to clients over HTTP/2 and HTTP/3. ORIGIN SERVER text/event-stream Cache-Control: no-cache X-Accel-Buffering: no CDN EDGE Buffering: DISABLED proxy_buffering off TCP_NODELAY + flush Idle timeout > 300s 1:1 or Fan-Out Client A — HTTP/2 EventSource + Last-Event-ID Client B — HTTP/3 (QUIC) Multiplexed stream Client C — HTTP/1.1 Keep-Alive long poll SSE CHUNKS
Figure 1: Transparent proxy mode — edge disables buffering and flushes each SSE event immediately over persistent HTTP/2/3 connections.

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

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

FeatureSSEWebSocketsLong Polling
DirectionServer → Client onlyBidirectionalServer → Client (emulated)
ProtocolHTTP (text/event-stream)ws/wss (Upgrade)HTTP
CDN FriendlinessHigh — standard HTTPLow — stateful upgradeMedium — high overhead
ReconnectionAutomatic + Last-Event-IDManualManual
Buffering RiskMust disable bufferingNo buffering (tunneled)Buffering less critical
HTTP/2/3 SupportNative multiplexingRequires extensionsWorks but inefficient
Best ForFeeds, notifications, LLM streamsChat, gaming, collaborationLegacy 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.

Frequently Asked Questions

Can CDNs cache Server-Sent Events?
No — SSE must not be cached. Use Cache-Control: no-cache, no-store and X-Accel-Buffering: no. The CDN should proxy the stream transparently, flushing each event immediately rather than storing it. Caching would serve stale events and break Last-Event-ID replay.
Why does my SSE stream buffer or delay at the CDN edge?
Default proxy buffering holds small chunks until a buffer (4–32KB) fills. Fix it by setting proxy_buffering off, X-Accel-Buffering: no, and disabling compression. Verify at every layer — origin, CDN edge, and any intermediate NGINX or load balancer.
Is SSE better than WebSockets for CDN delivery?
For server-to-client streaming, yes. SSE uses standard HTTP, supports automatic reconnection, works over HTTP/2 and HTTP/3 multiplexing, and requires no protocol upgrade. WebSockets are better only when you need true bidirectional, low-latency messaging.
How do CDNs handle SSE reconnections and Last-Event-ID?
Browsers automatically send Last-Event-ID on reconnect after a disconnect. Your CDN must forward this header to origin unchanged so the server can replay missed events from that ID. Do not let the edge generate or strip IDs — keep the edge stateless and transparent.

References & Standards: HTTP/2 specification · Cloudflare documentation