Long polling

Long polling is a technique for emulating server push over the request-response model of HyperText Transfer Protocol (HTTP). A client issues an HTTP request as usual, but instead of returning a response immediately, the server holds the request open until data becomes available or a timeout expires. The server then sends the response, and the client immediately re-issues the request. The result is a sequence of HTTP request-response cycles that approximates a continuous stream of updates from server to client.

Long polling predates WebSockets and Server-Sent Events (SSE), and was popularized in the mid-2000s under the umbrella term Comet. It remains useful as a fallback when those newer mechanisms are unavailable, for example behind corporate proxies that block WebSocket upgrades, or in environments where only plain HTTP is permitted.

How it works

The basic flow is as follows.

  1. The client sends an HTTP GET or POST request to a polling endpoint.
  2. The server accepts the request but does not produce a response yet. It suspends the request, typically registering it against an internal event source.
  3. When a relevant event occurs, or a maximum hold period elapses, the server completes the response with the new data, or with an empty body indicating no update.
  4. The client processes the response and immediately re-issues the request, restarting the cycle.

A typical server hold period is 20 to 60 seconds. Clients must handle both successful responses and timeouts, reconnecting promptly so that no events arrive in the gap between one request closing and the next opening.

Short polling vs long polling

In short polling (regular polling), the client repeatedly asks the server for updates at a fixed interval, and the server replies immediately each time, whether or not anything new exists. Short polling is simple but wasteful. Most requests return nothing, and real events are delayed by up to one polling interval.

Long polling reduces both waste and latency. The server only responds when there is something to send, so the client makes far fewer empty requests, and events are delivered as soon as they occur rather than at the next poll tick. The trade-off is that the server must hold many concurrent open requests, consuming file descriptors, memory, and server threads for the duration of each hold.

Implementing long polling

The following sketch shows a polling endpoint in Node.js-style pseudo-code.

app.get('/events', async (req, res) => {
  try {
    const event = await waitForNextEvent(req.query.since, 30000);
    res.json(event);
  } catch (timeout) {
    res.status(204).end(); // no update within the hold window
  }
});

The matching client loop.

async function poll() {
  while (true) {
    const res = await fetch(`/events?since=${lastEventId}`);
    if (res.status === 200) {
      const event = await res.json();
      handleEvent(event);
      lastEventId = event.id;
    }
    // Reconnect immediately; a small jitter delay avoids thundering herds.
    await sleep(Math.random() * 100);
  }
}

Several concerns arise in real implementations.

  • Reconnection gap. Events arriving between the end of one request and the start of the next are lost unless the server buffers them and the client sends a cursor, eg. an event ID or timestamp, with each request so the server can replay anything missed.
  • Connection limits. Browsers cap HTTP/1.1 connections per host at around six. A single long-polling request occupies one of those slots for its entire hold period, which can starve other requests on the same origin. HTTP/2 multiplexing removes this limit.
  • Server resource usage. Each held request ties up a server connection and, in thread-per-connection runtimes, a thread. Event-driven servers such as Node.js, Go, Netty, and async Python handle long polling far more efficiently, because a suspended request costs little beyond a small amount of memory.
  • Proxy and load-balancer timeouts. Intermediaries often impose their own idle timeouts, commonly 30 to 120 seconds, and close connections that appear stuck. Servers should respond just before the intermediary timeout fires, and clients must treat an unexpected close as a signal to reconnect.
  • Message ordering and backpressure. Because each event is delivered in its own HTTP response, there is no native flow control. A slow client can fall behind, and the server must decide whether to buffer, coalesce, or drop events.

When to use long polling

Long polling is a pragmatic choice in these situations.

  • The deployment environment blocks or interferes with WebSockets or SSE.
  • The application needs only occasional server-to-client updates rather than a high-frequency stream, so the per-request overhead of HTTP is acceptable.
  • The client can only speak plain HTTP, eg. some constrained IoT devices or legacy browsers.

For high-throughput, low-latency, or bidirectional communication, WebSockets are a better fit. For one-way server-to-client streams, SSE is simpler and has built-in browser reconnection. Long polling sits below both as a fallback.

See also