A Server-Sent Events endpoint answers 200 OK but with Content-Type: application/json - what does a conforming client do?
answer
- two conditions, both on the response
- status and media type, not one of them
- wrong type is terminal, not delayed
- network failure survivable, HTTP failure fatal
- 200 OK plus text/event-stream, nothing else
basics
~10 sA conforming client fails the connection permanently. It accepts only 200 OK carrying Content-Type: text/event-stream; anything else means no events are dispatched and no reconnection is ever attempted, so the feed simply stays empty.
solid answer
~40 sA Server-Sent Events client checks two things on the response before it dispatches anything: the HTTP status must be `200 OK`, and `Content-Type` must name the media type `text/event-stream`. A `200 OK` carrying `application/json` satisfies the first and fails the second, so the client *fails the connection*: it dispatches nothing and makes no further attempt to reconnect - this is not a retry with a long delay, it is the end. That is the opposite of what happens when the connection itself breaks, or when a stream that opened correctly later ends: those cases make the client reestablish the connection on its own. So the generosity people associate with this protocol applies to an unreliable network, not to a server that answered and answered wrong.
code
http · 9 linesGET /platform/6/arrivals HTTP/1.1
Host: boards.example
Accept: text/event-stream
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-cache
{"platform":"6","status":"on time"}go deeper
Remember the two conditions on the response: HTTP status 200 OK, and a Content-Type naming text/event-stream. Miss either one and the client delivers nothing at all.
Explain that an unacceptable response is terminal rather than retried, and contrast it with a dropped connection, which the client reestablishes by itself after its reconnection time.
Diagnose from the response rather than the client: read the status line and Content-Type of what actually arrived, including anything a reverse proxy or CDN edge substituted on the way.
Set the policy that a stream endpoint never answers an error status, because one bad response silently removes a client from the fleet permanently and produces no further traffic to alert on.
Server-Sent Events rides on an ordinary HTTP request. The client issues a GET with `Accept: text/event-stream`, and the server replies with a response body it simply never ends, writing `data:` lines and blank lines as events occur. Nothing about the exchange is special at the transport level, so the client has only the response itself to decide whether what came back is an event stream at all - and the specification makes that decision both strict and final. ## The two conditions checked before anything is dispatched A conforming client processes no events until both of these hold: - the HTTP status is `200 OK`; - the `Content-Type` response header names the media type `text/event-stream`. A `200 OK` carrying `application/json`, an HTML error page, or no `Content-Type` at all satisfies the first and fails the second. Either failure gives the same outcome, and the outcome is not "no events yet" - it is the end of that client's relationship with the endpoint. ## Failing the connection versus reestablishing it The whole rule turns on a distinction the specification draws in its own words. - **Reestablish the connection** - the client waits its current reconnection time and reissues the same request, carrying `Last-Event-ID` if it has one. This is the automatic reconnect the protocol is known for. - **Fail the connection** - the client makes no further attempt. Not after a delay, not with backoff, not later. The endpoint is finished as far as that client is concerned until the receiving application deliberately opens a new stream. | what came back | what a conforming client does | |---|---| | `200 OK` with `text/event-stream` | opens the stream and dispatches events | | `200 OK` with any other media type | fails the connection - no further attempt | | `204 No Content` | fails the connection; the defined way to say "stop" | | `301 Moved Permanently`, `307 Temporary Redirect` | follows the redirect as an ordinary HTTP request would | | `401 Unauthorized`, `403 Forbidden`, `503 Service Unavailable` | fails the connection, whatever caused the status | | connection refused, or dropped with no response | reestablishes after the reconnection time | | a good stream whose response body later ends | reestablishes after the reconnection time | Read that table once more in the direction that surprises people: a **network** failure is survivable and an **HTTP** failure is not. ## Why the symptom reads as "the client is broken" Nothing about this failure announces itself. The request completes normally - it is a finished request, not a pending one - so a live view of network traffic shows a healthy exchange. No event ever arrives, and, crucially, no second request ever arrives at the server either, so an operator watching access logs sees one request per client and then silence. Teams lose hours in the receiving application's code, which is the one place that is behaving correctly. The response the client judged is the response it actually received, which is not always the response the origin wrote. If a reverse proxy or a CDN edge substituted an error page, that page is the server's answer as far as the client is concerned, and the media type on it decides the outcome. ## Serving the endpoint so this cannot happen 1. Set `Content-Type: text/event-stream` explicitly on the streaming response rather than relying on a default. A default that produces `application/json` is the single most common cause of this failure. 2. Keep content negotiation away from a stream endpoint. An endpoint that can answer with two media types can answer with the wrong one, and one wrong answer is terminal per client. 3. Verify the response as the client sees it, through every intermediary, not as the origin wrote it. 4. Never answer a stream endpoint with an error status if you want that client back. If something is wrong and the client should return, either refuse the connection at the network level or open the stream normally and carry the condition inside an event. 5. When you genuinely want the client to stop, `204 No Content` says exactly that and the client will honour it. The design reason behind the strictness is worth stating: because a client reconnects by itself, a client that retried on every bad response would hammer a broken endpoint forever with no application code in the path to stop it. Making an unacceptable response terminal puts a hard ceiling on that loop, and hands the server one precise lever - answer correctly, or say `204 No Content` - instead of a retry storm nobody can call off.
- The same endpoint answers 200 OK with the right media type, and an hour later the response body ends. What happens?That is the other branch of the rule. A stream that opened correctly and then ended is treated as a connection that needs reestablishing: the client waits its current reconnection time and reissues the same request, carrying `Last-Event-ID` if one was set. Nothing about the earlier success is invalidated, and no application code is involved.
- A reverse proxy in front of the origin returns its own HTML error page with 502. Does the client care that the origin was fine?No. The client judges the response that reached it, and it has no way to know a different component produced it. A non-`200 OK` status fails the connection, so that client is gone even though the origin never saw the request. This is why an intermediary's error behaviour on a stream endpoint is worth configuring deliberately.
saying these in an interview costs you the question
- Believes any 200 response is usable as a stream
- Thinks a wrong media type just delays the first event
- Assumes the client keeps retrying whatever comes back
- Cannot distinguish a refused connection from an error response
- Blames the receiving application before reading the response headers