// Engineering Log

WebSockets, Long Polling, and SSE: how real-time data exchange works

Published on 2026-09-22

// Fast route

This article belongs to the topic Servers and infrastructure.

Chats, notifications, market quotes, collaborative editing, and neural network responses that appear as words — all of this requires the server to push data to the client immediately, without repeated requests. HTTP is originally designed differently: the client asks, the server replies. For real-time exchange over it, three approaches are used: Long Polling, Server-Sent Events (SSE) and WebSockets.

Long Polling

Long Polling is a technique, not a separate protocol. The client sends a regular HTTP request, and the server does not respond immediately but keeps the request open until data appears or a timeout expires. Upon receiving a response, the client immediately sends the next request.

Advantages:

  • works wherever HTTP works: through any proxies, firewalls, and old browsers;
  • does not require anything special on the server or the load balancer.

Disadvantages:

  • each message requires a full HTTP request with headers;
  • there is a gap between the response and the new request during which an event may be delayed;
  • thousands of pending requests load the server if it handles each request with a separate process or thread.

Today Long Polling is used as a fallback option when other methods are unavailable.

Server-Sent Events (SSE)

SSE is a standard browser mechanism for one-way event delivery from server to client. The client opens a regular HTTP connection, the server responds with the type text/event-stream and does not close it, sending events as they occur.

The browser has a built-in EventSource object for this:

javascript
const events = new EventSource("/api/events");

events.onmessage = (e) => {
  console.log("New event:", e.data);
};

events.addEventListener("order-status", (e) => {
  console.log("Order status:", e.data);
});

Advantages:

  • ordinary HTTP: no separate protocol or special load balancer configuration needed;
  • the browser automatically reconnects on disconnect;
  • named events are supported.

Limitations:

  • only from server to client; to send data to the server the client makes regular requests;
  • text only;
  • over HTTP/1.1 the browser keeps at most 6 concurrent connections per domain, and each open tab with SSE occupies one of them. Over HTTP/2 events use separate streams of a single connection, and this issue disappears.

SSE is a good choice for notifications, event feeds, progress indicators, and streaming neural network responses.

WebSockets

WebSocket (RFC 6455, 2011) is a separate protocol with two-way exchange over a single persistent connection.

The connection starts as a regular HTTP request with headers Upgrade: websocket and Sec-WebSocket-Key. The server responds with status 101 Switching Protocols, and then over the same TCP connection both sides exchange frames — text or binary. Addresses look like ws:// (port 80) and wss:// (port 443, with TLS). The protocol provides control frames ping and pong to check the connection.

Advantages:

  • bidirectional exchange without new requests;
  • minimal per-message overhead;
  • binary data support.

Characteristics:

  • the connection lives long, so proxies and load balancers must be configured not to drop it;
  • the application must handle reconnection, delivery acknowledgment, and restoration of missed messages — the browser does not do this;
  • each open connection consumes server resources, so a server designed for many concurrent connections is required for large numbers of clients.

WebSocket over HTTP/2

Originally WebSocket worked only over HTTP/1.1. RFC 8441 (September 2018) describes starting WebSocket inside a single HTTP/2 stream: the server announces support with the SETTINGS_ENABLE_CONNECT_PROTOCOL parameter, and the client opens a stream with the extended CONNECT method and the pseudo-header :protocol: websocket. This way a WebSocket connection and regular requests go over a single TCP connection. Whether this works in your stack depends on the browser, proxies, and application — by default most proxies still proxy WebSocket over HTTP/1.1.

Which to choose

TaskApproach
Notifications, feeds, job progress, streaming server responseSSE
Chat, collaborative editing, games, trading terminals — frequent bidirectional dataWebSocket
Clients behind strict corporate proxies, old devicesLong Polling as a fallback
Data changes every few minutesRegular periodic requests — simpler than the other options

Many libraries (for example, Socket.IO) choose the best available transport themselves and fall back to Long Polling if WebSocket is unavailable.

Proxies and load balancers

Proxy settings are needed in two areas: forwarding the upgrade headers for WebSocket and timeouts for long connections.

  • Nginx. The Upgrade and Connection headers are not forwarded to the application automatically; they must be set explicitly; by default nginx closes a connection if there is no data for 60 seconds, and for SSE you need to disable response buffering. A working configuration with a map block and troubleshooting is in the article “Proxy servers: Part 2 — Nginx”.
  • HAProxy. WebSocket is supported without additional modules, but the timeout tunnel directive is required, otherwise connections are closed according to normal timeouts. An example is in the article “Proxy servers: Part 3 — HAProxy”.
  • Caddy. The reverse_proxy directive proxies WebSocket without additional configuration, and responses with type text/event-stream are sent to the client immediately, without buffering. On configuration reload WebSocket connections are closed by default; this behavior is changed by the stream_timeout and stream_close_delay parameters.

Typical errors

  • Connection drops exactly after one minute. The proxy timeout for inactive connections fired. Raise the timeout and send pings from the application.
  • SSE events arrive in a batch at the end. The proxy is buffering the response. Disable buffering for that endpoint.
  • WebSocket works locally but not behind the load balancer. The Upgrade and Connection headers were not forwarded, or the load balancer is distributing a single client’s requests across multiple servers while the application stores state in memory. You need client affinity (sticky sessions) to a server or a shared state store.
  • Missed messages after reconnection. The protocol does not guarantee this — the application needs message identifiers and retransmission.

// Similar task

If you are dealing with something similar

This article belongs to one of the main working topics. You can keep reading on the topic, go to the homepage to understand what I do, or open the service pages directly.

Article topic

Servers and infrastructure

VPS, Linux, web stack, migrations, hosting, databases, and core operations.

Typical tasks behind this topic

  • Move a site or service to a new server
  • Set up Linux, Nginx, databases, and backups
  • Figure out why the system behaves unstably

// Next step

If you need help with this topic, not just another article, it is better to go straight to the service page. The homepage and topic collection stay available as secondary routes.

Open services

// Reviews

Related reviews

apande

apande

Configuring Nginx and OpenCart

2024-09-07 · ★ 5/5

A very powerful buyer
kireevk

kireevk

Diagnosing Nginx Proxy Manager in a Docker container and resolving the issue

2024-03-15 · ★ 5/5

Settled-in customer

// Contact

Need help?

Get in touch with me and I'll help solve the problem

I reply within one business day (03:00-13:00 GMT)

Или оставьте заявку здесь:

Confirm that you are not a bot.

Write and get a quick reply