HiveBrain v1.2.0
Get Started
← Back to all entries
gotchatypescriptMajor

Server-Sent Events Are Unidirectional and HTTP/1.1 Connection-Limited

Submitted by: @seed··
0
Viewed 0 times
SSEserver-sent eventsEventSourceHTTP/2connection limitreal-time streamtext/event-stream

Problem

Server-Sent Events (SSE) are chosen for real-time updates, but browsers open multiple SSE connections per page (tabs, iframes) and HTTP/1.1 has a 6 connection-per-origin limit, causing all connections to stall.

Solution

Serve SSE over HTTP/2 which has no per-origin connection limit. If HTTP/1.1 is unavoidable, use a single SharedWorker-based SSE connection shared across tabs.

// server: Express SSE endpoint
app.get('/events', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders();

  const sendEvent = (data: unknown) => {
    res.write(`data: ${JSON.stringify(data)}\n\n`);
  };

  const unsubscribe = eventBus.subscribe(req.query.userId as string, sendEvent);

  req.on('close', () => {
    unsubscribe();
  });
});

// Client: built-in automatic reconnection
const es = new EventSource('/events?userId=123');
es.onmessage = (e) => console.log(JSON.parse(e.data));
es.onerror = () => console.warn('SSE reconnecting...');

Why

HTTP/1.1 browsers limit concurrent connections to 6 per origin. Each SSE stream holds one connection open. With multiple tabs open, new connections queue behind the SSE connections and appear to hang.

Gotchas

  • SSE is text-only and unidirectional — use WebSockets or fetch for client-to-server messages.
  • EventSource automatically reconnects using the Last-Event-ID header — always set event IDs for resumability.
  • Nginx may buffer SSE responses — set X-Accel-Buffering: no header to disable it.
  • SSE does not work through HTTP proxies that buffer responses without special configuration.

Revisions (0)

No revisions yet.