# text/event-stream: Server-Sent Events Format and Examples

> Server-sent events: a long-lived response in which the server pushes messages as lines of text, and the browser's EventSource reads them as they arrive.

- MIME type: `text/event-stream`
- Used for: Streaming response
- Parameters: `charset` — always UTF-8, so it can be left out
- Specification: [HTML Standard: server-sent events](https://html.spec.whatwg.org/multipage/server-sent-events.html)

## Example

```http
HTTP/1.1 200 OK
Content-Type: text/event-stream
Cache-Control: no-cache

retry: 5000

event: price
id: 41
data: {"symbol":"BTC","usd":67210}

: keep-alive comment

data: a message with the default event type

```

## Send it from the server

### Express

```
app.get("/events", (req, res) => {
  res.set({ "Content-Type": "text/event-stream", "Cache-Control": "no-cache" });
  res.flushHeaders();

  const timer = setInterval(() => res.write(`data: ${Date.now()}\n\n`), 1000);
  req.on("close", () => clearInterval(timer));
});
```

### Web standard Response (Next.js route handlers, Deno, Bun, Workers)

```
export async function GET() {
  const enc = new TextEncoder();
  const stream = new ReadableStream({
    start(controller) {
      controller.enqueue(enc.encode("data: hello\n\n"));
    },
  });
  return new Response(stream, {
    headers: { "Content-Type": "text/event-stream", "Cache-Control": "no-cache" },
  });
}
```

### nginx in front of the app

```
location /events {
  proxy_pass http://app;
  proxy_http_version 1.1;
  proxy_set_header Connection "";
  proxy_buffering off;      # or send X-Accel-Buffering: no from the app
  proxy_read_timeout 1h;
}
```

## Read it in the client

### Browser (EventSource)

```
const events = new EventSource("/events");
events.onmessage = (e) => console.log(e.data);
events.addEventListener("price", (e) => console.log(JSON.parse(e.data)));
```

### curl (-N turns off output buffering)

```
curl -N https://example.com/events
```

## Good to know

- Reverse proxies buffer responses by default, so events arrive in bursts or only when the stream ends. Turn buffering off for the route — `proxy_buffering off` or an `X-Accel-Buffering: no` response header for nginx — and make sure compression middleware flushes after each event or skips the stream.
- EventSource reconnects on its own and sends the last `id:` it received in a `Last-Event-ID` request header, so the server can resume where it left off.
- EventSource can only make GET requests and can't set headers. AI APIs that stream over POST use the same event format, but clients read it with `fetch` and a stream reader instead.
- Over HTTP/1.1, browsers allow about six connections per origin, and every open EventSource holds one. HTTP/2 removes that limit.

## FAQ

### What is the difference between server-sent events and WebSockets?

Server-sent events are one-way, from server to client, over an ordinary HTTP response, with reconnection built in. WebSockets are two-way and need an upgrade handshake. For notifications, live feeds and streamed AI responses, server-sent events are simpler.

### Why do my server-sent events arrive all at once?

Something between the server and the browser is buffering the response, usually a reverse proxy or compression middleware. Disable buffering for the stream (`X-Accel-Buffering: no` for nginx) and flush after each event.

### What do the lines in an event stream mean?

`data:` carries the payload (several data lines are joined with newlines), `event:` names the event type, `id:` sets the ID used for resuming, `retry:` sets the reconnection delay in milliseconds, and a line starting with `:` is a comment, often sent as a keep-alive. A blank line ends each event.

## Related types

- [application/problem+json](https://www.devkult.com/mime/problem-json.md)

Open the interactive page: https://www.devkult.com/mime/text-event-stream