← Home ← Codex ← DEBT
Browse by Category
+ added · updated 7d
← Back to glossary

EventSource API — Server-Sent Events (Client Side)

JavaScript HTML5 Intermediate
debt(d7/e3/b3/t7)
d7 Detectability Operational debt — how invisible misuse is to your safety net

Closest to 'only careful code review or runtime testing' (d7). No detection tools are specified in detection_hints. The common mistakes — missing double newline, forgetting es.close(), Nginx buffering, missing CORS headers — are all silent failures that manifest only at runtime (dropped connections, stalled streams, CORS errors in browser console). No standard linter rule catches these; they surface only under real server conditions or careful review.

e3 Effort Remediation debt — work required to fix once spotted

Closest to 'simple parameterised fix' (e3). The quick_fix is a small, localised change: add a header on the server side, ensure double newlines, call es.close() on unmount. Each individual mistake is a targeted one-to-three line fix (e.g., add X-Accel-Buffering header, fix newline terminator, add withCredentials), but taken together across server config and client code they are small refactors within a single component rather than single-line patches.

b3 Burden Structural debt — long-term weight of choosing wrong

Closest to 'localised tax' (b3). The applies_to scope is web only, and EventSource usage is typically confined to a specific streaming feature (e.g., one endpoint and one client component). It doesn't permeate the entire codebase — only the SSE endpoint and the consuming component carry the configuration burden (Nginx config, PHP time limits, CORS headers). The rest of the application is unaffected.

t7 Trap Cognitive debt — how counter-intuitive correct behaviour is

Closest to 'serious trap' (t7). The misconception field explicitly states that developers treat EventSource and WebSockets as interchangeable. This contradicts mental models carried from WebSocket experience — SSE is server-to-client only, uses plain HTTP, auto-reconnects, but cannot send client data. Choosing it when bidirectional communication is needed requires an architectural swap. The Nginx buffering issue and CORS credential default are additional non-obvious surprises that contradict reasonable assumptions.

About DEBT scoring →

Also Known As

EventSource SSE client server-sent events JavaScript event stream

TL;DR

EventSource is the browser API for consuming Server-Sent Events (SSE) — a one-directional server-to-client stream over HTTP that automatically reconnects, ideal for live feeds, notifications, and streaming LLM responses.

Explanation

new EventSource(url) opens a persistent HTTP connection where the server pushes text/event-stream formatted messages. The browser automatically reconnects with exponential backoff if the connection drops, sending the Last-Event-ID header so the server can resume from where it left off. EventSource only supports GET requests and plain text — no binary, no custom headers without a polyfill. For bidirectional communication use WebSockets. For streaming responses from a PHP backend, the server writes 'data: <message>\n\n' and flushes — each double-newline is one event. Named events (event: type\n) allow different listeners. SSE works through proxies better than WebSockets and requires no upgrade handshake.

Common Misconception

EventSource and WebSockets are interchangeable. EventSource is server-to-client only and uses plain HTTP — simpler to deploy, works through proxies, auto-reconnects. WebSockets are bidirectional and require a persistent connection upgrade. Use SSE for push-only feeds; WebSockets when the client also needs to send real-time data.

Why It Matters

EventSource is the simplest way to stream real-time updates from a PHP backend — live order status, streaming AI responses, progress bars for long jobs. It requires no WebSocket server, works through standard HTTP infrastructure, and auto-reconnects without any application code.

Common Mistakes

  • Not disabling Nginx output buffering — Nginx buffers upstream responses by default; add 'X-Accel-Buffering: no' header or proxy_buffering off in nginx config.
  • Forgetting the double newline to terminate events — each SSE message must end with \n\n; a single \n continues the current event's data.
  • Not handling the CORS case — EventSource sends credentials by default only to same-origin; for cross-origin, pass { withCredentials: true } and set appropriate CORS headers.
  • Long-lived PHP scripts without max_execution_time override — SSE streams run indefinitely; set set_time_limit(0) at the start of the streaming script.
  • Not closing EventSource when navigating away — forgetting to call es.close() leaves connections open on the server; attach cleanup to beforeunload or component unmount.

Code Examples

✗ Vulnerable
// ❌ Polling — inefficient, delayed, hammers the server
setInterval(async () => {
    const res  = await fetch('/api/notifications');
    const data = await res.json();
    if (data.length) showNotifications(data);
}, 3000); // New HTTP request every 3 seconds even when nothing changed
✓ Fixed
// ✅ EventSource — persistent connection, server pushes when ready
const es = new EventSource('/api/notifications/stream');

es.onmessage = (event) => {
    const notification = JSON.parse(event.data);
    showNotification(notification);
};

// Named events — different handlers per event type
es.addEventListener('order-shipped', (event) => {
    updateOrderStatus(JSON.parse(event.data));
});

es.onerror = (err) => {
    // Browser auto-reconnects — this fires on reconnect attempts too
    if (es.readyState === EventSource.CLOSED) {
        console.error('SSE connection permanently closed');
    }
};

// Close when done (e.g. user navigates away)
window.addEventListener('beforeunload', () => es.close());

// PHP side:
// header('Content-Type: text/event-stream');
// header('Cache-Control: no-cache');
// header('X-Accel-Buffering: no'); // Nginx: disable buffering
// while (true) {
//     echo 'data: ' . json_encode(getLatestEvent()) . "\n\n";
//     ob_flush(); flush();
//     sleep(1);
// }

Added 23 Mar 2026
Edited 2 May 2026
Views 54
AI edit PF Media Bot Claude Opus 4.5 on common_mistakes · 2 May 2026
Edits history 1 edit
  1. common_mistakes PF Media Bot Claude Opus 4.5 · 2 May 2026
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
0 pings T 1 ping W 1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 0 pings T 0 pings W 1 ping T 2 pings F 1 ping S 0 pings S 0 pings M 1 ping T 0 pings W 1 ping T 2 pings F 0 pings S 0 pings S 1 ping M 0 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 1 ping T 0 pings W
No pings yet today
Bing 1
Amazonbot 10 Google 6 Scrapy 4 Ahrefs 3 Bing 3 SEMrush 3 Meta AI 2 Perplexity 2 Claude 2 ChatGPT 1 Majestic 1 PetalBot 1
crawler 32 crawler_json 6
DEV INTEL Tools & Severity
⚙ Fix effort: Low
⚡ Quick Fix
For a PHP streaming endpoint, set header('Content-Type: text/event-stream') and echo 'data: ' . $message . "\n\n" with ob_flush() + flush() in a loop. On the client, new EventSource('/stream').onmessage = e => console.log(e.data).
📦 Applies To
javascript HTML5 web


✓ schema.org compliant