← Home ← Codex ← DEBT ← Engine
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 103
Rate this term
No ratings yet
🤖 AI Guestbook educational data only
| |
Last 30 days
1 ping T 0 pings F 0 pings S 0 pings S 0 pings M 1 ping T 0 pings W 0 pings T 0 pings F 1 ping S 2 pings S 2 pings M 2 pings T 0 pings W 0 pings T 1 ping F 2 pings S 0 pings S 0 pings M 2 pings T 0 pings W 0 pings T 0 pings F 0 pings S 1 ping S 0 pings M 0 pings T 1 ping W 0 pings T 1 ping F
Bing 1
No pings yesterday
Amazonbot 10 Google 8 SEMrush 7 PetalBot 7 Bing 6 Ahrefs 5 Scrapy 4 Claude 3 Meta AI 2 Perplexity 2 Twitter/X 2 Applebot 2 ChatGPT 1 Majestic 1 Brave Search 1 Baidu 1
crawler 56 crawler_json 6
🧱 FUNDAMENTALS — new to this? Start with the ground floor.
JavaScript javascript The programming language of the browser — it reads and modifies the page (the DOM), reacts to user events, and fetches data without reloading.

JavaScript is the only language browsers execute, so every interactive behaviour on the web goes through it. Its two defining traits — single-threaded event loop and loose typing (== coercion) — explain the majority of both its bugs and its design patterns.

💡 Default to const, use === always, and reach for let only when a value genuinely reassigns.

Ask Codex about JavaScript →
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