78 Server-Sent Events Interview Questions and Answers (2026)

Real-time features are everywhere now, and interviewers know SSE is the lean way to push live data over plain HTTP. If you can't explain the wire format, reconnection with Last-Event-ID, or when to pick SSE over WebSockets, that's an instant red flag. The bar has risen — hand-wavy familiarity no longer cuts it.
This guide gives you 78 questions with concise, interview-ready answers and code where it actually helps. They're worked from Junior to Mid to Senior, so you build from fundamentals to protocol details, scaling, and security. Work through them and walk in ready to prove real fluency.
Q1.What are Server-Sent Events (SSE)?
Server-Sent Events (SSE) are a browser standard that lets a server push a continuous, one-way stream of text updates to a client over a single long-lived HTTP connection.
One-directional: Data flows server to client only; the client opens the connection and then just listens.
Built on plain HTTP: The response uses the text/event-stream content type and stays open, streaming events as they occur.
Browser API: Consumed on the client with the EventSource object, which handles parsing and reconnection automatically.
Q2.Explain the core concept of Server-Sent Events and the problem they aim to solve compared to traditional polling.
SSE keeps one HTTP connection open so the server can push data the instant it's available, eliminating the wasted requests and latency of traditional polling.
The polling problem:
The client repeatedly asks "anything new?" on a timer, most requests return nothing, wasting bandwidth and server cycles.
Updates are delayed by the polling interval, so there's an inherent latency tradeoff.
The SSE approach:
One connection stays open; the server sends events only when something changes (push, not pull).
Near real-time delivery with no redundant round trips.
Core concept: Invert control: instead of the client asking, the server notifies over a persistent stream.
Q3.Explain how Server-Sent Events work, including the underlying HTTP connection.
HTTP connection.The client makes an ordinary HTTP GET request, and the server responds with Content-Type: text/event-stream and keeps the connection open, writing text-encoded events over time instead of closing after one response.
Handshake: Client requests via EventSource; server keeps the response body open and streams chunks.
Wire format: Each message is UTF-8 text with fields like data:, event:, id:, and retry:, separated by a blank line.
Reconnection: If dropped, the browser reconnects and sends the Last-Event-ID header so the server can resume from where it left off.
Transport note: It's regular HTTP, so it works with existing infrastructure; use HTTP/2 to avoid the per-domain connection limit.
Q4.What are the main advantages of using Server-Sent Events?
SSE gives you simple, real-time server push using plain HTTP, with automatic reconnection built into the browser and no special protocol to manage.
Simplicity: Just an HTTP endpoint and the EventSource API; no handshake protocol like WebSockets.
Automatic reconnection: The browser reconnects on drop and resumes via Last-Event-ID, giving free resilience.
Infrastructure friendly: Runs over standard HTTP/HTTPS, so proxies, firewalls, and load balancers generally handle it without special config.
Efficient for push: One connection, no polling overhead, low latency for one-way updates.
Q5.What are the key characteristics or features of SSE?
SSE is defined by a persistent HTTP stream carrying text events, a simple structured message format, and browser-managed reconnection with resumable state.
Persistent connection: A single long-lived HTTP response with text/event-stream.
Structured messages: Fields data, event (named event types), id, and retry (reconnect delay in ms).
Auto-reconnect with resume: Browser retries and replays Last-Event-ID so missed events can be re-sent.
Event routing on the client: Named events dispatch to specific listeners via addEventListener.
Text/UTF-8 payloads: Commonly JSON strings inside data: lines.
Q6.What is the most important difference between WebSockets and SSE regarding communication direction?
The core difference is directionality: WebSockets are full-duplex (both sides send freely over one connection), while SSE is unidirectional, streaming only from server to client.
SSE: one-way: Server pushes events; the client can only receive on that channel and must use a separate HTTP request to send data upstream.
WebSockets: two-way: Either side sends messages at any time over the same persistent connection.
Implication: If you only need server push, SSE is simpler; if the client must also stream to the server in real time, WebSockets are the right tool.
Q7.What character encoding is typically used for SSE messages?
SSE streams are always encoded as UTF-8; the spec mandates it, so both server and client interpret the text/event-stream body as UTF-8 text.
UTF-8 is required: The EventSource specification defines the decoding as UTF-8; other charsets in the header are ignored.
Implication for servers: Encode payloads (including JSON) as UTF-8; you don't need a charset parameter because it's fixed.
Line endings: Lines may end with \n, \r, or \r\n, but the payload text itself must be valid UTF-8.
Q8.Explain the purpose of the readyState property in the EventSource API.
readyState property in the EventSource API.readyState reports the current lifecycle state of the connection, letting you distinguish connecting, open, and permanently closed.
Three numeric states:
EventSource.CONNECTING (0): connecting or reconnecting.
EventSource.OPEN (1): connected and receiving events.
EventSource.CLOSED (2): closed and not retrying (after close() or a fatal error).
Why it matters: During a temporary drop it reads CONNECTING, not CLOSED, so you can show a reconnecting UI vs a truly-dead one.
Q9.How does a client subscribe to an SSE stream using the EventSource API in JavaScript?
EventSource API in JavaScript?You subscribe by constructing an EventSource with the stream URL and attaching handlers; the browser opens the connection and delivers events as they arrive.
Create the connection:
new EventSource('/events') immediately begins the GET request.
For cross-origin with cookies: new EventSource(url, { withCredentials: true }).
Attach handlers: onmessage for default events, addEventListener() for named ones, onopen/onerror for lifecycle.
Q10.How can a client explicitly close an SSE connection?
Call close() on the EventSource instance: it terminates the connection and, crucially, stops the browser's automatic reconnection.
Client-side close: es.close() sets readyState to CLOSED (2) and prevents retries.
When to call it: On component unmount or page navigation to avoid leaked connections and needless reconnect loops.
Server-side stop: Responding with HTTP 204 (or a non-retryable status) tells the browser not to reconnect either.
Q11.How does an unnamed (default) event in the stream map to the client-side handler, and what is the 'message' event type?
'message' event type?An event with no event: field is a default event of type message, so it's delivered to the onmessage handler (or a listener registered for "message").
Unnamed events default to message: A block with only data: lines fires onmessage.
Named events need explicit listeners: If the server sends event: update, onmessage will NOT fire; you must call es.addEventListener("update", ...).
Payload lands on event.data as a string regardless of the event type.
Q12.What is the browser support situation for EventSource, and when would you reach for a polyfill?
EventSource, and when would you reach for a polyfill?EventSource is supported in all modern browsers (Chrome, Firefox, Safari, Edge); the notable historical gap is Internet Explorer and older Edge, which never implemented it, so a polyfill is only needed for legacy targets or to unlock features the native API lacks.
Modern support is broad: Evergreen browsers have had it for years; no polyfill needed for typical apps.
Reach for a polyfill when:
You must support IE11 or very old browsers.
You need POST bodies, custom headers, or Authorization: libraries like event-source-polyfill or a fetch-based implementation add these.
Alternative: The Fetch API with a ReadableStream can consume text/event-stream manually, giving full control over method and headers.
Q13.What is the Content-Type header required for an SSE stream, and why is it important?
Content-Type header required for an SSE stream, and why is it important?SSE responses must use Content-Type: text/event-stream, which tells the browser to treat the body as a stream of events and parse it according to the SSE format.
It activates the SSE parser: The EventSource API relies on this MIME type; the browser reads incrementally and dispatches message events as data: blocks arrive.
Wrong type breaks streaming: With text/plain or application/json, the client buffers as a normal response and never fires incremental events; EventSource will error on a mismatched type.
Encoding: The stream is UTF-8 by spec, so no explicit charset is required.
Q14.What is the default reconnection interval if the server never sends a retry: field?
retry: field?When the server never sends a retry: field, the reconnection delay is left to the browser, and the commonly used default is roughly 3 seconds (3000 ms), though it is implementation-defined.
Default is browser-defined: The spec does not mandate an exact number; each EventSource implementation picks one, and ~3000 ms is typical.
Overriding it: Send a retry: line with milliseconds (e.g. retry: 10000) to set the delay; it persists for subsequent reconnects until changed.
Don't hardcode assumptions: If you need a predictable interval, set retry: explicitly rather than relying on the default.
Q15.What are some common use cases where Server-Sent Events would be a suitable choice?
SSE fits any scenario needing one-way, server-to-client push of text data over a single long-lived HTTP connection, where you don't need the client to also stream data back.
Live feeds and notifications: News tickers, sports scores, social notifications, activity feeds.
Real-time dashboards / monitoring: Metrics, logs, and status updates pushed as they change.
Streaming LLM/token output: Chat completions delivered incrementally as tokens are generated.
Progress and job status: Long-running upload/processing/build progress bars.
When SSE beats WebSockets: Data flows only one way, you want plain HTTP (proxies, auth), and you value built-in auto-reconnect and event IDs.
Q16.What are the limitations of Server-Sent Events?
SSE is one-way, text-only, and subject to per-browser connection limits, which rule it out for truly bidirectional or binary workloads.
Server-to-client only: For client-to-server messages you still need separate HTTP requests; no full duplex.
Text only: Payloads are UTF-8; binary must be encoded (e.g. base64), adding overhead.
Connection limits: Over HTTP/1.1 browsers cap ~6 connections per domain; HTTP/2 multiplexing mitigates this.
No native IE support: Legacy Internet Explorer lacks EventSource and needs a polyfill.
Idle connection handling: Intermediaries may kill idle streams; heartbeat comments are often needed to keep them alive.
Q17.Compare and contrast Server-Sent Events with WebSockets, discussing their directionality, underlying protocols, automatic reconnection capabilities, data format support, and typical use cases where each would be preferred.
SSE is a lightweight one-way HTTP text stream with built-in reconnection, while WebSockets are a full-duplex binary-capable protocol over its own upgraded connection: choose based on whether you need two-way communication.
Directionality:
SSE: server to client only.
WebSockets: bidirectional (full duplex).
Underlying protocol:
SSE: plain HTTP/HTTPS, no protocol switch.
WebSockets: an HTTP Upgrade handshake to the ws:// / wss:// protocol.
Reconnection:
SSE: automatic, with Last-Event-ID resume.
WebSockets: none built in; you implement it yourself.
Data format:
SSE: UTF-8 text only.
WebSockets: text and binary frames.
Typical use cases:
SSE: live feeds, notifications, stock tickers, log/LLM token streaming.
WebSockets: chat, multiplayer games, collaborative editing, anything interactive.
Q18.When would you choose SSE over WebSockets, and vice versa? Provide specific use cases for each.
Choose SSE when updates flow mainly one way and you want simplicity and free reconnection; choose WebSockets when both sides must exchange messages continuously or you need binary/low-latency two-way traffic.
Prefer SSE when:
The server pushes and the client mostly listens: news/sports feeds, dashboards, notifications.
You want minimal setup over standard HTTP and automatic reconnection.
Streaming incremental text, e.g. LLM token-by-token responses.
Prefer WebSockets when:
Both directions send frequently: chat, multiplayer games, live collaboration (docs, whiteboards).
You need binary payloads or the lowest latency two-way channel.
Rule of thumb: One-way push, start with SSE; genuine interactivity, reach for WebSockets.
Q19.When is SSE a poor fit for a real-time communication scenario?
SSE is a poor fit whenever the scenario needs frequent client-to-server messaging, binary data, or truly bidirectional low-latency exchange: it is a one-way, text-only stream layered on HTTP.
Bidirectional or high-frequency client input: SSE only pushes server to client; the client must send via separate requests, which is awkward for chat, gaming, or collaborative editing.
Binary payloads: SSE frames are UTF-8 text only; sending images or audio means base64 overhead. Prefer WebSockets.
Many concurrent connections over HTTP/1.1: Browsers cap ~6 connections per domain, so an app opening many streams can starve other requests (HTTP/2 multiplexing mitigates this).
Ultra-low-latency, high-throughput duplex: Round-trip request/response for client messages adds latency WebSockets avoid.
Q20.What are the key differences between SSE, long polling, and short polling?
All three deliver server updates over HTTP, but they differ in how the connection is held: short polling reconnects repeatedly, long polling holds a request until data arrives, and SSE keeps one persistent stream that pushes many events.
Short polling: Client requests on a fixed interval; server replies immediately (empty if nothing new). Simple but wasteful and high-latency.
Long polling: Server holds the request open until data is ready, then responds; client immediately re-requests. Lower latency but one response per connection.
SSE: One long-lived connection streams many events over time via text/event-stream, with built-in auto-reconnect and event IDs.
Efficiency ranking: SSE > long polling > short polling for server-push, in latency and overhead.
Q21.What factors should you consider when choosing between SSE, WebSockets, and long polling for real-time updates?
Choose based on communication direction, data type, connection scale, and infrastructure support: pick the simplest technique that meets the latency and directionality needs.
Direction of data flow: Server-to-client only: SSE. Full duplex: WebSockets. Occasional/simple: long polling.
Data format: Text/JSON favors SSE; binary favors WebSockets.
Reconnection and reliability: SSE gives automatic reconnect and Last-Event-ID resume for free; WebSockets you build yourself.
Infrastructure and simplicity: SSE is plain HTTP: works with proxies, load balancers, and auth cookies easily. WebSockets need upgrade support and often special config.
Scale: Consider connection limits (HTTP/1.1 per-domain cap) and how many persistent connections your servers can hold.
Q22.How does SSE leverage standard HTTP compared to WebSockets which upgrade the connection?
SSE is just a normal HTTP response that never ends: the server sets a streaming content type and keeps writing events, so no protocol switch is needed, unlike WebSockets which upgrade the TCP connection to a separate protocol.
SSE stays HTTP: Response uses Content-Type: text/event-stream and stays open, streaming chunks. Standard headers, cookies, and auth apply unchanged.
WebSockets upgrade: Client sends Upgrade: websocket; after the handshake the connection speaks the ws:// protocol, no longer HTTP.
Practical consequence: SSE passes through most proxies, CDNs, and load balancers with no special config; WebSocket upgrades often need explicit support.
Q23.What are common use cases where Server-Sent Events are a good fit, and conversely, scenarios where SSE would be a poor choice?
SSE shines for one-way, server-driven text updates and is a poor choice when the client must send frequently or data is binary/bidirectional.
Good fits:
Live feeds: news, sports scores, stock tickers.
Notifications and status updates (build progress, job completion).
Streaming AI/LLM token output to a browser.
Dashboards and live monitoring metrics.
Poor fits:
Chat, multiplayer games, collaborative editing (need bidirectional/low-latency).
Binary transfer (video, audio, file streams).
Apps opening many simultaneous streams under HTTP/1.1 connection limits.
Q24.How would you implement real-time updates for an API without using WebSockets, specifically considering SSE?
Expose an endpoint that returns a streaming text/event-stream response and have the client subscribe with EventSource: the server writes formatted events as data becomes available and the browser handles reconnection automatically.
Server side:
Set Content-Type: text/event-stream, disable buffering, and keep the connection open.
Emit each message as data: lines ending with a blank line; add id: and event: as needed.
Client side: Use new EventSource('/stream') and listen with onmessage or named event listeners.
Resilience: Send id: so the browser resends Last-Event-ID on reconnect, letting you resume from the last delivered event.
Q25.What would be the downside of using SSE when you do need bidirectional communication, relying on regular AJAX requests for client-to-server messages?
You can bolt client-to-server messaging onto SSE with separate AJAX/fetch POST requests, but you lose the efficiency and coherence of a true bidirectional channel: two independent transports must be coordinated, and each upstream message pays full HTTP request overhead.
Two channels to manage: The SSE stream (server-to-client) and the AJAX calls (client-to-server) are unrelated connections, so you must correlate them yourself (session IDs, request/response matching).
Per-message overhead upstream: Every client message is a fresh HTTP request with headers, and possibly TLS and auth cost, versus a WebSocket frame that reuses one open socket.
Higher latency for interactive use: Fine for occasional actions, but poor for chat, gaming, or collaborative editing where messages fly both ways rapidly.
Connection limits: Older HTTP/1.1 browsers cap connections per domain; a held-open SSE stream plus AJAX calls can bump against that limit (mitigated by HTTP/2).
Verdict: acceptable for mostly-download traffic with rare upstream events; if bidirectional traffic is frequent and latency-sensitive, use WebSockets.
Q26.Describe the text/event-stream wire format for Server-Sent Events, including how individual events are delimited, how multi-line data is handled, and the purpose of fields like data:, event:, id:, and retry:.
text/event-stream wire format for Server-Sent Events, including how individual events are delimited, how multi-line data is handled, and the purpose of fields like data:, event:, id:, and retry:.An SSE stream is a UTF-8 text body of type text/event-stream made of events separated by blank lines; each event is one or more field: value lines, and the browser's EventSource parses these fields to build the message.
Event delimiting: A single blank line (a double newline, \n\n) marks the end of one event and dispatches it to the client.
data: the payload: The actual message content; multiple data: lines in one event are joined with \n to form multi-line data.
event: the type: Names a custom event so the client can listen with addEventListener('name', ...); default is the generic message event.
id: the event ID: Sets the stream's last event ID; on reconnect the browser sends it back in the Last-Event-ID header so the server can resume.
retry: the reconnection delay: An integer of milliseconds telling the client how long to wait before reconnecting after a drop.
Lines starting with : are comments (ignored), often used as heartbeats.
Q27.How are multi-line data messages handled in the SSE format?
Multi-line data is sent as several consecutive data: lines within the same event; the client concatenates their values with a newline (\n) between them to reconstruct the full payload.
Each line contributes one segment: Every data: line's value is appended, joined by \n; there is no trailing newline after the last segment.
Why it exists: A raw newline can't appear inside a single field value, so splitting across data: lines is how you transmit line breaks or pretty-printed JSON.
The whole set is still one event, terminated by the trailing blank line.
Q28.Describe the text/event-stream wire format and the required MIME type for an SSE response.
text/event-stream wire format and the required MIME type for an SSE response.An SSE response must set the Content-Type header to text/event-stream, and its body is a stream of UTF-8 text events built from field: value lines separated by blank lines.
Required MIME type: text/event-stream; the browser's EventSource only treats the response as SSE if this type is present.
Supporting headers: Cache-Control: no-cache to prevent stale caching, and typically Connection: keep-alive to hold the stream open.
Wire format of the body: Lines like data:, event:, id:, retry:; each event ends with a blank line (double newline).
Q29.What is the significance of the blank line (double newline) in the SSE protocol?
The blank line (a double newline, \n\n) is the event delimiter: it signals that the current event is complete and should be dispatched to the client. Without it, the parser keeps buffering fields as part of the same, unfinished event.
It triggers dispatch: On seeing the blank line, the client fires the accumulated data:, event:, and id: as one event.
Missing it stalls delivery: A common bug: writing data: x\n without the second newline means the client never receives the event until more data flushes it.
It separates independent events, so back-to-back messages each need their own trailing blank line.
Q30.How are comment or heartbeat lines indicated in the SSE stream, and why are they used?
A line that begins with a colon (:) is a comment: the client ignores its content entirely. These are commonly sent as heartbeats to keep the connection and any intermediaries alive during quiet periods.
Syntax: A leading : marks the line as a comment, e.g. : keep-alive; no field is parsed from it.
Why heartbeats matter:
Proxies, load balancers, and browsers may drop idle connections; a periodic comment keeps traffic flowing without emitting a real event.
They also let the server detect a dead client when the write eventually fails.
Zero application impact: Because comments dispatch nothing, EventSource handlers never fire for them.
Q31.How can multiple distinct data streams be multiplexed over a single SSE connection using event types?
You multiplex by tagging each message with an event: field so a single connection carries several logical channels, and the client registers a separate listener per event name.
The event: field names the stream:
Messages with different names travel over the same TCP/HTTP connection but are dispatched to different handlers.
A message with no event: field defaults to the message type.
Client subscribes per channel: Use addEventListener('priceUpdate', ...) and addEventListener('news', ...) on the same EventSource.
Route or filter payloads by name: Each event's data: is independent, so distinct schemas can coexist without interleaving problems.
Caveat: it is still one ordered stream: A slow consumer or backpressure affects all channels equally; there is no per-channel flow control.
Q32.Explain the EventSource API in the browser for consuming Server-Sent Events, including how a connection is established, how events are handled (e.g. onmessage, named events), and how the connection can be closed.
EventSource API in the browser for consuming Server-Sent Events, including how a connection is established, how events are handled (e.g. onmessage, named events), and how the connection can be closed.The EventSource API opens a persistent HTTP connection to a stream URL, parses the incoming text/event-stream format, and fires DOM events you handle with onmessage, named listeners, and onerror.
Establishing the connection:
new EventSource(url) issues a GET with Accept: text/event-stream and keeps it open.
onopen fires once the connection is established.
Handling events:
onmessage receives unnamed (default message) events.
Named events need addEventListener('name', handler).
The payload is in event.data (always a string; parse JSON yourself).
Errors and reconnection: onerror fires on drops; the browser auto-reconnects unless you close it.
Closing: es.close() terminates the connection and stops reconnection attempts.
Q33.How does the browser's EventSource API handle automatic reconnection when the connection is lost?
EventSource API handle automatic reconnection when the connection is lost?Reconnection is built in: when the connection drops, the browser waits a retry interval and automatically reopens the stream, optionally resuming from the last event via Last-Event-ID.
Automatic retry: On a network drop, the browser fires onerror, sets readyState to CONNECTING, and retries after a delay.
Server controls the delay: A retry: field (milliseconds) in the stream sets the reconnection interval.
Resuming with Last-Event-ID: If the server sent id: fields, the browser sends the last one back in the Last-Event-ID header so the server can replay missed events.
When it stops: Calling close() halts retries; a fatal HTTP status (e.g. 204) also stops reconnection.
Q34.How can you listen for custom/named events with SSE using the EventSource API?
EventSource API?Custom events are named on the server with the event: field and consumed on the client with addEventListener() using that exact name; onmessage only catches unnamed events.
Server emits a named event: An event: userJoined line followed by its data: line.
Client registers a matching listener: es.addEventListener('userJoined', handler); the payload is in event.data.
Common gotcha: Named events do NOT trigger onmessage; forgetting the dedicated listener means those events silently disappear.
Q35.Does the EventSource API send cookies with the SSE request, and how does that interact with the withCredentials flag for cross-origin streams?
EventSource API send cookies with the SSE request, and how does that interact with the withCredentials flag for cross-origin streams?By default EventSource sends cookies for same-origin requests, but for cross-origin streams it only sends credentials (cookies, HTTP auth) if you set withCredentials: true, and the server must cooperate with matching CORS headers.
Same-origin requests: Cookies for that origin are sent automatically, like any normal GET.
Cross-origin requests:
Pass the option: new EventSource(url, { withCredentials: true }).
The server must return Access-Control-Allow-Credentials: true and an explicit Access-Control-Allow-Origin (not *), or the browser blocks the response.
Without withCredentials, cross-origin streams are anonymous: no cookies, so session-based auth won't work.
Q36.When the EventSource error event fires, does it always mean the connection is permanently closed, or will it reconnect? How do you distinguish recoverable from fatal errors?
EventSource error event fires, does it always mean the connection is permanently closed, or will it reconnect? How do you distinguish recoverable from fatal errors?An error event does NOT always mean a permanent close: EventSource auto-reconnects on transient failures, and you distinguish the cases by inspecting readyState when the handler fires.
Recoverable (will reconnect): When readyState === EventSource.CONNECTING (0), the browser has already scheduled a reconnect (e.g. dropped connection, server closed the stream normally).
Fatal (stays closed): When readyState === EventSource.CLOSED (2), reconnection has stopped, typically after a fatal HTTP status or an explicit es.close().
The event itself carries little detail: There's no HTTP status on the error object; you infer state from readyState and server-side logs.
Q37.How does EventSource react to different HTTP status codes returned by the server (e.g. 200 vs 204 vs 4xx/5xx)? Which cause a retry and which stop reconnection?
EventSource react to different HTTP status codes returned by the server (e.g. 200 vs 204 vs 4xx/5xx)? Which cause a retry and which stop reconnection?EventSource treats status codes as a signal: a valid 200 with the right content type keeps the stream open, certain codes trigger reconnection, and others permanently fail the connection.
200 OK: Must have Content-Type: text/event-stream; otherwise the connection fails and does NOT reconnect.
Reconnect on drop: If a healthy stream ends or the network drops, the client reconnects after the retry interval.
204 No Content: Signals the client to stop: EventSource closes and does NOT retry.
Redirects (301/307): Followed to the new URL.
Fatal errors (4xx/5xx like 403, 404, 500): The connection fails and reconnection stops; readyState goes to CLOSED.
Q38.Why is the native EventSource limited to GET requests with no request body, and what constraints does that place on your API design?
EventSource limited to GET requests with no request body, and what constraints does that place on your API design?The native EventSource spec deliberately only issues a GET with no body: SSE is a simple, cacheable, reconnectable one-way stream, and the auto-reconnect machinery (replaying with Last-Event-ID) relies on the request being idempotent and fully described by its URL.
Why GET-only: The browser must be able to re-issue the exact request on reconnect; a bodyless GET is trivially replayable.
No custom headers either: You can't set Authorization or other headers on native EventSource, only cookies via withCredentials.
API design consequences:
Pass all parameters in the query string or path.
Do auth via cookies, or via a token in the query string (weigh the logging/leak risk).
If you truly need POST/bodies/headers, use a fetch-stream based polyfill instead of native EventSource.
Q39.What are the essential server-side HTTP headers required for a Server-Sent Events response (e.g. Content-Type, Cache-Control, Connection), and why is each important for maintaining a long-lived stream?
Content-Type, Cache-Control, Connection), and why is each important for maintaining a long-lived stream?An SSE response needs headers that declare the event-stream format, prevent caching, and keep the connection alive so the server can push bytes over time instead of closing after one payload.
Content-Type: text/event-stream: Tells the browser to parse the body as an SSE stream and hand events to EventSource; without it the client won't treat the response as a stream.
Cache-Control: no-cache: Stops browsers and proxies from caching or replaying the stream, so clients always get live events rather than a stale snapshot.
Connection: keep-alive: Signals the connection should stay open for the long-lived stream (HTTP/1.1); largely implicit but important when intermediaries are involved. Irrelevant under HTTP/2 where multiplexing replaces it.
X-Accel-Buffering: no: A common practical addition: disables proxy buffering (e.g. nginx) so events are delivered immediately instead of being held until a buffer fills.
Q40.Why is sending periodic heartbeats or keep-alive comments important in an SSE stream?
Heartbeats (usually SSE comment lines) keep the connection demonstrably alive: they stop idle-timeout kills by proxies and let the server discover dead clients when no real events are flowing.
Prevent idle timeouts: Proxies, load balancers, and browsers close connections that sit silent too long; a periodic byte resets those timers.
Detect dead peers: Writing the heartbeat fails if the client vanished, letting the server trigger cleanup instead of leaking a connection.
Use a comment line: A line starting with : (e.g. : keep-alive\n\n) is ignored by EventSource, so it doesn't fire a message event.
Q41.What server-side considerations are important for keeping the SSE connection open and sending periodic heartbeats/keep-alive comments?
Keeping an SSE connection open means holding a long-lived response, sending periodic keep-alive comments to survive timeouts, and managing per-connection resources so many open streams don't exhaust the server.
Keep the response open: Return a streaming/generator response that never returns; don't set a fixed Content-Length so the connection isn't closed after N bytes.
Send heartbeats on an interval: Emit a comment line (e.g. every 15-30s) to reset proxy/idle timers and to surface dead clients via write failure.
Use async concurrency: Each SSE client ties up a connection for its lifetime; an async server (ASGI/event loop) scales to many idle streams far better than thread-per-connection.
Mind infrastructure limits: Load balancer/proxy idle timeouts, worker connection caps, and buffering settings all must accommodate long-lived, incrementally-flushed responses.
Q42.How is SSE built on top of HTTP chunked transfer encoding, and what does the server actually keep doing to the response body over time?
Over HTTP/1.1, SSE rides on chunked transfer encoding: instead of one finite body, the server declares Transfer-Encoding: chunked and keeps appending new chunks (each an event) to the same never-completed response body.
No Content-Length: The server doesn't know the total size, so it omits Content-Length and streams chunks; the connection stays open indefinitely.
Each event is one or more chunks: The server writes a data: ...\n\n block and flushes it as a chunk; the client parses bytes as they arrive rather than waiting for the end.
The body never terminates: The server never sends the final zero-length chunk that would end the response; it just keeps producing events until either side closes.
HTTP/2 differs: There's no chunked encoding; the stream is framed as DATA frames on a long-lived stream, but the SSE application semantics are identical.
Q43.How would you test or debug an SSE endpoint from the command line (e.g. with curl) and verify the stream format is correct?
curl) and verify the stream format is correct?Use curl with the SSE Accept header and watch the raw bytes stream in real time: a correct stream is a long-lived response with Content-Type: text/event-stream and events framed as field: value lines separated by blank lines.
Basic command:
Run curl -N http://localhost:8000/stream: -N disables buffering so events appear as they arrive rather than all at once.
Add -H "Accept: text/event-stream" to mimic a real EventSource client.
Inspect headers: Use curl -v (or -i) to confirm Content-Type: text/event-stream, Cache-Control: no-cache, and Connection: keep-alive.
Verify the wire format:
Each event is data: (plus optional event:, id:, retry:) lines, then a blank line to dispatch.
Multi-line data is multiple data: lines; a stray missing blank line means the event never fires on the client.
Test reconnection semantics: Send -H "Last-Event-ID: 42" to check the server resumes from the right point.
Q44.Why should SSE responses set Cache-Control: no-cache, and what could go wrong if an intermediary caches the stream?
Cache-Control: no-cache, and what could go wrong if an intermediary caches the stream?SSE is a live, unbounded stream, so it must never be cached: Cache-Control: no-cache tells browsers and intermediaries to always fetch fresh data rather than replay a stored copy. A cache in the middle would break the stream by buffering or serving stale bytes.
Why no-cache:
The response body never ends, so caching semantics designed for finite documents don't apply.
Clients must receive events as they happen, not a snapshot from an earlier point in time.
What breaks if an intermediary caches:
Buffering: a proxy that waits to store the whole response hangs forever, so no events ever reach the client.
Stale replay: a cached copy serves old events to new clients and misses everything after the cache point.
Shared corruption: one user's event stream could leak to another if cached by a shared proxy.
Related headers: Set X-Accel-Buffering: no to stop proxies like nginx from buffering, and avoid gzip buffering on the stream.
Q45.How does the Last-Event-ID HTTP header work, and what role does it play in reliable message delivery and resuming a stream after a disconnection?
Last-Event-ID HTTP header work, and what role does it play in reliable message delivery and resuming a stream after a disconnection?The Last-Event-ID header is the browser's built-in resume mechanism: when the server assigns each event an id:, the client remembers the last one and, on automatic reconnection, sends it back so the server can replay only what was missed.
How it flows:
Server emits events with id: <n> lines.
The EventSource stores the most recent id internally.
On disconnect it reconnects and automatically sends Last-Event-ID: <n> as a request header.
The server reads it and resends events with a higher id.
Its role in reliability:
Enables at-least-once resume: gaps caused by dropped connections can be filled.
Requires the server to keep a buffer or log of recent events keyed by id.
Caveats:
Fully automatic only for the browser EventSource; manual clients must send the header themselves.
If the server can't reproduce old events (buffer evicted), the client silently misses them.
Q46.How can the server tune the client's reconnection behavior using the retry field?
retry field?The server sends a retry: field (in milliseconds) as part of the stream to tell the client how long to wait before reconnecting after a drop, overriding the browser's default (typically around 3 seconds).
Syntax:
A line like retry: 10000 sets the reconnection delay to 10 seconds.
It can be sent alone or alongside data, and persists for subsequent reconnections.
When to tune it:
Increase it under load or during maintenance to reduce reconnection pressure.
Decrease it for latency-sensitive streams that must recover fast.
Limits:
It only controls the delay, not whether reconnection happens (the client decides that).
A fixed value alone doesn't prevent synchronized reconnection storms: add jitter server-side.
Q47.How does SSE handle automatic reconnection, and how can a server ensure message delivery or resume a stream after a client disconnects?
SSE reconnection is automatic and built into the browser: when the connection drops, the EventSource waits the retry interval and reopens the connection on its own. To make delivery reliable across that gap, the server assigns event ids and replays missed events using the resent Last-Event-ID.
Automatic reconnection:
The client detects the closed stream and retries after the retry: delay, no application code needed.
This is a key advantage over raw WebSockets, where you reconnect manually.
Ensuring delivery / resume:
Tag every event with id:.
Persist recent events in a buffer, log, or stream store (e.g. Redis stream).
On reconnect, read Last-Event-ID and replay everything after it.
Practical notes:
Buffer size bounds how long a client can be offline and still catch up.
Send periodic comment heartbeats (: ping) to keep the connection alive and detect drops quickly.
Q48.Can SSE transmit binary data? Explain why or why not, and the implications if binary data needs to be sent.
No, SSE cannot natively carry binary data: the protocol is a UTF-8, text-line format where events are delimited by newlines, so raw binary would corrupt the stream. To send binary you must encode it into text.
Why it's text-only:
The stream is decoded as UTF-8 and parsed line by line using data:, event:, id: fields separated by newlines.
Newline bytes and non-UTF-8 sequences in binary would be misinterpreted as delimiters or invalid characters.
Workaround: Encode binary as text, typically Base64, and decode on the client.
Implications:
Base64 adds ~33% size overhead plus encode/decode CPU cost.
For heavy or frequent binary payloads, prefer WebSockets (binary frames) or a separate HTTP endpoint for the blob.
Q49.What are the performance limitations of SSE?
SSE's main limits come from being a long-lived, one-way HTTP connection: each open stream consumes server resources, and unlike a shared multiplexed transport, plain HTTP/1.1 caps how many streams a browser can open per domain.
One connection held open per client: Every subscriber ties up a socket, a file descriptor, and (on blocking stacks) potentially a thread for the connection's lifetime.
Browser per-domain connection cap (HTTP/1.1): Typically 6 connections per host, so 6 open SSE streams can starve other requests to the same domain. HTTP/2 multiplexing largely fixes this.
Unidirectional only: Server to client only; client-to-server needs a separate request, so it's not a fit for chatty bidirectional workloads (use WebSockets there).
Text-only payloads: Data is UTF-8 text; binary must be base64-encoded, adding ~33% size overhead.
Scaling cost is horizontal fan-out: Broadcasting to many clients means holding many connections and pushing the same event N times, requiring an event bus / pub-sub behind the servers.
Q50.What is the overhead of SSE compared to polling, considering the open TCP connection?
TCP connection?SSE's overhead is one persistent TCP (and TLS) connection kept idle between events, whereas polling repeatedly pays connection setup, headers, and handshake costs. For frequent updates SSE is far cheaper; for rare updates polling's cost is only paid occasionally, but SSE still avoids latency and redundant round-trips.
Polling overhead is per-request: Every poll re-sends full HTTP headers, cookies, and (on new connections) TCP + TLS handshakes, often returning "nothing new."
SSE overhead is per-connection, amortized: Headers and handshake are paid once; afterward each event is just a small data: framed text chunk.
The tradeoff is idle-connection cost: An idle SSE connection consumes a socket/FD and buffers even when nothing is sent; polling frees resources between requests.
Latency: SSE pushes instantly; polling adds up to one interval of delay per update.
Rule of thumb: Frequent or low-latency updates favor SSE; sparse, infrequent updates with huge client counts can favor polling to avoid holding idle sockets.
Q51.How is SSE being used in modern applications, particularly for streaming Large Language Model responses?
SSE has become the de facto transport for streaming LLM responses: the server sends each generated token (or chunk) as an event, so the UI renders text progressively instead of waiting for the full completion. This is exactly the one-way, incremental, text-over-HTTP pattern SSE was designed for.
Why SSE fits LLMs:
Generation is inherently one-directional and incremental; SSE streams chunks as they're produced, cutting perceived latency.
Plain HTTP means it works through existing proxies, gateways, and auth.
How providers use it: OpenAI-style APIs send data: lines with JSON deltas, terminated by a sentinel like data: [DONE].
Practical notes:
Set Content-Type: text/event-stream and disable proxy buffering so chunks flush immediately.
Payloads are usually JSON deltas rather than raw text, requiring client-side accumulation.
Many clients use fetch-based streaming rather than raw EventSource to send auth headers and POST prompts.
Q52.Discuss CORS considerations when using the EventSource API.
EventSource API.An EventSource connecting to a different origin is a cross-origin request subject to CORS: the server must return the appropriate Access-Control-Allow-Origin headers or the browser blocks the connection.
Same-origin is exempt: No CORS headers needed when the stream is on the same origin as the page.
Cross-origin requires server headers: Server must send Access-Control-Allow-Origin matching the page origin (or * when not using credentials).
Credentials mode:
Set new EventSource(url, { withCredentials: true }) to send cookies.
Then the server must echo a specific origin (not *) and send Access-Control-Allow-Credentials: true.
No preflight normally: A plain GET with simple headers is a 'simple request', so no OPTIONS preflight; custom headers (via fetch polyfills) would trigger one.
Q53.What are the security considerations for SSE?
SSE runs over ordinary HTTP, so it inherits standard web security concerns: use TLS, authenticate and authorize every stream, validate origins, and guard against resource exhaustion from long-lived connections.
Transport encryption: Always serve over https; HTTP/2 also helps avoid the 6-connection-per-domain limit.
Authentication & authorization: Verify identity on connect and confirm the user may receive each event; never push data across tenant/user boundaries.
Origin / CORS controls: Restrict allowed origins; with cookie auth, defend against CSRF (SameSite cookies, origin checks).
Injection in the stream: Sanitize/escape server-pushed data; the client must treat it as data, not inject raw into the DOM (XSS risk).
Resource exhaustion / DoS: Cap connections per user, apply timeouts and rate limits, since each client holds an open connection.
Token leakage: Avoid long-lived tokens in query strings (logs/history); prefer short-lived one-time tokens or cookies.
Q54.Discuss the trade-offs of using SSE compared to other real-time communication techniques like WebSockets or traditional HTTP streaming/chunked responses.
SSE trades power for simplicity: it is easier to deploy and resilient by default, but limited to one-way text, whereas WebSockets offer full-duplex binary at higher complexity, and raw HTTP chunked streaming lacks SSE's event framing and reconnect semantics.
SSE advantages: Runs over standard HTTP, auto-reconnect, event IDs, and a simple browser API (EventSource).
SSE limitations: One-way, text-only, and subject to HTTP/1.1 connection limits.
WebSockets: Full-duplex, binary-capable, lower per-message overhead; but require an upgrade handshake, custom reconnect logic, and can be harder through proxies.
Plain HTTP chunked streaming: Also one-way over HTTP, but you must invent your own message framing and reconnection. SSE is essentially chunked streaming with a defined protocol.
Q55.What are the operational overheads of WebSockets compared to SSE?
WebSockets give you a full-duplex connection but cost more to run: they use a custom protocol upgraded from HTTP, so proxies, load balancers, and stateful connection tracking all need extra care compared to SSE, which is just a long-lived HTTP response.
Protocol and infrastructure friction:
WebSockets upgrade from HTTP to the ws:///wss:// protocol; some proxies, firewalls, and CDNs don't handle the upgrade gracefully.
SSE rides on ordinary HTTP, so existing HTTP infrastructure (auth, compression, caching layers) works unchanged.
Connection state and scaling:
A WebSocket is a persistent stateful socket the server must track and manage on both directions; scaling to many connections needs careful memory and fan-out design.
SSE is unidirectional, so the server just writes to a response stream, which is simpler to reason about.
No built-in reconnection:
WebSockets have no native auto-reconnect or event-ID replay; you build heartbeats and reconnection logic yourself.
SSE gives automatic reconnection and Last-Event-ID resumption for free.
Load balancing complexity: Long-lived WebSocket connections complicate rolling deploys and sticky-session routing.
Payoff: you accept this overhead only when you genuinely need low-latency bidirectional or binary messaging.
Q56.When should you use the native EventSource API versus fetch + ReadableStream for SSE, especially concerning authentication requirements?
EventSource API versus fetch + ReadableStream for SSE, especially concerning authentication requirements?Use native EventSource for simple GET streams where its built-in parsing and reconnection are enough; reach for fetch + ReadableStream when you need custom headers (notably Authorization), non-GET methods, or a request body.
Native EventSource: convenient but limited:
Free auto-reconnect, Last-Event-ID, and event parsing.
GET only, no custom headers, so bearer-token auth is impossible; you fall back to cookies or a query-string token.
fetch + ReadableStream: flexible but manual:
Set any headers (Authorization: Bearer ...), use POST, send a body.
You must parse text/event-stream frames and implement reconnection yourself.
Rule of thumb: Cookie-based auth and a plain stream: native. Header-based tokens or richer requests: fetch (or a library like @microsoft/fetch-event-source).
Q57.What happens to open SSE connections when a browser tab is backgrounded or the device sleeps, and how might that affect your design?
When a tab is backgrounded or the device sleeps, browsers commonly throttle or suspend the connection: the stream may be paused, dropped, or reconnected when the tab becomes visible again, so your design can't assume a continuously live socket.
What browsers do:
Background tabs are throttled; on sleep or mobile suspension the connection is often torn down entirely.
On resume, EventSource typically auto-reconnects (and sends Last-Event-ID if you set event IDs).
Design implications:
Emit id: on events so the server can replay what was missed via the Last-Event-ID header.
Make handlers idempotent: a reconnect may redeliver an event.
Use visibilitychange to refetch state on return, rather than trusting the stream stayed alive.
Don't rely on SSE for background delivery (e.g. push notifications); use the Push API for that.
Q58.How does the EventSource client parse and buffer partial events that arrive split across multiple network chunks?
EventSource client parse and buffer partial events that arrive split across multiple network chunks?The EventSource parser buffers incoming bytes and only dispatches an event when it sees the blank line (a double newline) that terminates an event block, so partial data split across TCP chunks is safely reassembled before any handler fires.
Line-based accumulation: The client reads the stream, splitting on line terminators (\n, \r, or \r\n), and holds any incomplete trailing line until more bytes arrive.
Dispatch on blank line: An empty line marks end-of-event: only then are the buffered data: lines joined (with \n) and delivered.
Field parsing: Each complete line is split at the first : into field name and value; lines starting with : are comments/keep-alives and ignored.
Consequence for servers: You must terminate each event with a blank line, or the client buffers indefinitely and nothing fires.
Q59.How can a server detect when an SSE client has disconnected and perform necessary cleanup?
Q60.Explain the importance of flushing and disabling buffering on the server side when implementing SSE, and why it is necessary.
Q61.How do you handle idle timeouts and missed events during client disconnects in SSE?
Q62.What are the guarantees around message delivery and ordering in SSE, especially during reconnections?
Q63.What happens if a client disconnects between SSE messages, and what are the implications for critical workflows regarding missed events?
Q64.How can you prevent a reconnection storm during a server outage, and what role does the retry: field play in this?
retry: field play in this?Q65.How does the last event id persist across events that don't include an id: field, and what value is sent in Last-Event-ID on reconnect?
id: field, and what value is sent in Last-Event-ID on reconnect?Q66.How can a server reset or clear the client's last event id so it stops sending a stale Last-Event-ID on reconnect?
Last-Event-ID on reconnect?Q67.Where should you keep the state needed to replay missed events after a reconnect, and what are the trade-offs of buffering per-connection versus using a durable log?
Q68.How can proxy servers or load balancers affect SSE streams, and what measures can be taken to mitigate issues like buffering or idle timeouts?
Q69.How can you horizontally scale a system that uses many concurrent SSE connections, and what is the role of a shared pub/sub backplane?
Q70.What are the connection limitations of SSE, especially over HTTP/1.1, and how does HTTP/2 mitigate this?
HTTP/1.1, and how does HTTP/2 mitigate this?Q71.What's the maximum number of concurrent SSE connections a server can handle, and what factors influence this?
Q72.How can you handle backpressure if a client's SSE connection is slow?
Q73.What are sticky sessions in the context of scaling SSE, and why might they be relevant?
Q74.When scaling Server-Sent Events to a large number of concurrent connections, what architectural considerations are important, particularly regarding load balancers, proxy buffering, and detecting client disconnects on the server side?
Q75.Can you use gzip or other content compression on an SSE stream, and what are the caveats?
gzip or other content compression on an SSE stream, and what are the caveats?Q76.Beyond raising the per-domain connection limit, what other benefits does running SSE over HTTP/2 provide?
HTTP/2 provide?Q77.What are the implications of holding thousands of idle open TCP connections for server memory, file descriptors, and thread/event-loop models?
Q78.What are the challenges of authenticating SSE connections, given that EventSource cannot set custom HTTP headers, and what are common workarounds?
EventSource cannot set custom HTTP headers, and what are common workarounds?