“The application needs real-time updates” is not enough information to choose a communication mechanism.
A deployment dashboard receives a continuous server-generated log. A chat application sends messages in both directions. A background export only needs to announce completion once. All three can feel real-time to a user, but their traffic shapes are different.
Long polling, Server-Sent Events, and WebSockets can each deliver an update soon after it occurs. The meaningful questions are:
The best choice is the simplest mechanism that satisfies those requirements reliably.
The three mechanisms differ mainly in what stays open and what must be repeated.
Long polling holds one HTTP request until an event or timeout occurs. The response completes, and the client immediately starts another request.
SSE holds one HTTP response open and writes multiple UTF-8 events into its body.
WebSocket establishes a persistent framed channel on which either endpoint can send messages independently.
These are application communication patterns. Their underlying transport connections can be reused or multiplexed depending on the HTTP version and networking stack.
Long polling extends ordinary HTTP request-response timing. The client sends a request, but the server does not respond immediately when no update is available. It waits until:
When the response arrives, the client processes it and starts another long poll.
A response could contain JSON:
If no event arrives before the application timeout, the server can return an empty result:
The client immediately starts another request. On network or server errors, it retries with backoff rather than entering a tight failure loop.
With ordinary short polling, the server responds immediately whether or not anything changed:
Update latency depends on the polling interval. A five-second interval can deliver an event almost immediately or nearly five seconds late.
With long polling, a request is already waiting when the event occurs:
This usually provides much lower update latency without sending repeated empty requests at a short interval.
No request should wait forever. Servers, gateways, load balancers, clients, and network address translation devices all impose timeouts.
The application chooses a long-poll timeout below the shortest relevant infrastructure timeout. If a proxy ends idle upstream requests after 30 seconds, an application might complete its poll earlier and let the client renew it.
There is no universal safe timeout. It must be tested through the real production path. A longer timeout reduces empty response churn, while a shorter timeout detects broken paths sooner and is more likely to survive restrictive intermediaries.
There is a small interval between receiving a response and establishing the next poll. An event that occurs during this interval must not disappear.
A cursor makes the request describe the client's position:
The server returns events after 143, whether they appeared before or after the new request arrived. This requires retained event state or a way to construct a fresh snapshot.
Without a cursor or server-side queue, long polling can lose events in the gap between requests. Starting multiple overlapping polls may appear to close the gap, but it creates ordering, duplication, connection-consumption, and cancellation problems. One logical outstanding poll per subscription is usually easier to reason about.
The most important differences are summarized below.
| Dimension | Long Polling | Server-Sent Events | WebSocket |
|---|---|---|---|
| Communication direction | Server replies to a waiting request; client uses HTTP requests | Server to client; client uses separate HTTP requests | Full-duplex on one channel |
| Connection pattern | Repeated held requests and complete responses | One long-lived HTTP response | One long-lived framed connection |
| Browser API | fetch() or another HTTP client | EventSource | WebSocket |
| Payload | Any HTTP representation, including JSON or binary | UTF-8 text event records | Text or binary messages |
| Per-update overhead | HTTP response plus next request | Small text field prefixes | Compact frame header after handshake |
| Reconnection | Application loop | Automatic in EventSource | Application logic |
| Resume support | Application cursor | id and Last-Event-ID, plus server replay | Application cursor or acknowledgment |
| Intermediary concerns | Request timeouts and repeated routing | Response buffering and idle timeouts | Upgrade or extended CONNECT support and idle timeouts |
| Backpressure | One response naturally gates the next poll | Continuous stream needs bounded server queues | Continuous channel needs bounded queues |
| Typical fit | Infrequent updates or restrictive infrastructure | Primarily server-to-client text updates | Frequent bidirectional or binary interaction |
The table describes protocol capabilities, not complete delivery guarantees. Authentication, replay storage, authorization, idempotency, and fan-out remain application responsibilities in all three designs.
Loading simulation...
Directionality is usually the first decision.
SSE directly matches feeds where the server produces most of the traffic:
The client can still send commands with ordinary HTTP. For example, a browser might receive order updates through SSE and submit POST /orders separately.
Long polling has the same logical direction for updates but closes the response after each delivery or timeout. It is useful when the environment handles complete HTTP responses more reliably than partial streaming responses.
WebSocket is designed for both endpoints to send whenever needed. It avoids modeling each client message as a separate HTTP request and each server message as a response or stream event.
This fits interactions such as:
A bidirectional product does not automatically require WebSocket. A chat with infrequent client messages could use HTTP POST for sending and SSE for receiving. WebSocket becomes more compelling when both directions are frequent, latency-sensitive, or part of one shared session.
All three can deliver a server event quickly while their connection or request is active.
SSE and WebSocket keep a continuous delivery path. Long polling has a waiting request most of the time, so its steady-state event latency can also be close to one network transit. Its extra latency appears during the brief renewal gap after each response and during retries.
Their overhead differs more as message frequency rises.
Long polling repeats HTTP processing for every response and replacement request. Headers may include cookies, tracing data, routing information, and authentication. HTTP/2 header compression and persistent transport reuse reduce wire cost, but the application, gateway, and server still process repeated request lifecycles.
SSE pays the HTTP setup once per connection and uses text field prefixes for each event:
WebSocket pays an opening handshake and then uses compact frames. This is valuable for many small messages, although masking, TLS records, application envelopes, and network packets mean the real cost is more than the minimum frame header alone.
For sparse updates, these byte differences are rarely the most important factor. Compatibility, reconnect behavior, and implementation simplicity usually matter more. At high message rates, repeated long-poll request processing becomes increasingly expensive.
Suppose 100,000 connected clients have no updates and renew a long poll every 25 seconds. If renewals are evenly spread, timeout alone completes and replaces roughly:
The server also holds close to 100,000 requests concurrently.
If every client receives one update per second and each response carries one event, the design can approach 100,000 completed responses and replacement requests per second. Batching can reduce that churn, but it can increase latency.
SSE and WebSocket still require roughly 100,000 live client streams or channels and must deliver the same 100,000 updates per second. They avoid renewing an HTTP request for every update, but they do not eliminate the fan-out work or concurrent connection state.
Long polling is sometimes described as avoiding persistent connections. That is misleading.
While waiting, every client has an outstanding request and occupies a transport stream or connection, server request state, a timeout, and often subscription state. Long polling reduces unnecessary responses compared with short polling; it does not remove concurrent waiters.
SSE and WebSocket also keep state for each connected client. Their connection lifetime is more explicit, and a single connection can deliver many messages without repeatedly rebuilding request context.
At scale, none of the three should reserve a blocked operating-system thread for every idle client. The server needs an I/O model that can hold many waiting requests or connections efficiently. The exact concurrency implementation belongs to the server platform, but the capacity question is shared by all three approaches.
HTTP/2 and HTTP/3 multiplexing can let many long polls or SSE streams share fewer underlying connections. WebSocket can also use modern HTTP bootstrapping when the client, server, and intermediaries support it. Multiplexing reduces transport-connection contention; it does not eliminate per-subscription memory and processing.
Real traffic often passes through a CDN, web application firewall, API gateway, reverse proxy, and load balancer before reaching an application.
Long polling uses complete HTTP responses. This makes it broadly compatible with HTTP-aware infrastructure, but every intermediary must permit the request to remain pending long enough. Cache behavior must be disabled, and gateway timeouts must be coordinated with the application timeout.
Because each response completes, every new request can be routed to a different application instance. This works well when instances share the event store and the cursor contains enough resume state.
SSE is ordinary HTTP, but intermediaries must forward partial response data promptly. Proxy buffering, response compression, caching, and idle timeouts can turn immediate events into delayed batches or terminate the stream.
The connection stays attached to the serving gateway or application process for its lifetime. After reconnecting, it can land on another instance and resume using the last event ID if shared replay state exists.
A classic WebSocket connection requires support for the HTTP upgrade and then for long-lived bidirectional traffic. HTTP/2 and HTTP/3 use extended connection mechanisms rather than the classic 101 transition, so support must be verified end to end.
After establishment, the connection stays attached to the gateway and process that own it. Proxies need suitable idle timeouts and must forward Close, Ping, Pong, text, and binary frames correctly.
Modern infrastructure commonly supports all three, but configuration defaults still matter. A successful local test against the application server does not prove that the production path will behave the same way.
Network connections fail. Mobile devices change networks, laptops sleep, proxies expire idle state, and deployments restart servers. The selection should include the recovery design, not only the healthy connection.
The application owns the request loop. After a timeout, it polls again immediately. After an error, it normally uses exponential backoff with jitter.
A cursor tells the next server where to resume. Keeping only one logical poll in flight helps preserve ordering; overlapping requests can return in a different order from the events they represent.
Browser EventSource reconnects automatically. An SSE id becomes the Last-Event-ID request header, which gives the server a standard cursor mechanism.
This is convenient but not sufficient by itself. The server still needs replay storage, retention rules, and behavior for an expired cursor.
The browser WebSocket API does not reconnect automatically. The application creates a new connection, reauthenticates, restores subscriptions, and sends a cursor or requests a fresh snapshot.
If the application needs confirmation that a command was processed, it defines acknowledgment messages and idempotency keys.
In all three cases, a connection can fail at an ambiguous moment:
The sender may not know whether the consumer processed it. Replay can prevent gaps but introduce duplicates. Preventing both loss and duplicate effects requires application-level IDs, durable state, acknowledgments where appropriate, and idempotent handling.
Ordering also has a boundary. SSE and WebSocket preserve event or message order within one live stream. Long polling can preserve order with a single request and cursor. After reconnecting, ordering depends on the server's replay or snapshot logic.
Backpressure asks what happens when updates are produced faster than one client can consume them.
Long polling provides a degree of natural pacing when the client starts the next request only after processing the previous response. The server may batch events after the cursor. However, the retained backlog can still grow, and a large response can overwhelm a slow client.
SSE continuously writes events into one response. Browser EventSource exposes no application-controlled flow signal, so the server needs bounded queues and a policy to combine updates, drop replaceable data, or disconnect slow clients for later resume.
WebSocket libraries also need queue limits. The stable browser API exposes bufferedAmount for outbound bytes but does not automatically solve inbound application backpressure.
The correct policy depends on event meaning:
Protocol choice does not decide which data is safe to drop.
All three should use encrypted transport in production: HTTPS for long polling and SSE, and wss for WebSocket.
Long polling implemented with fetch() has the normal HTTP request surface. It can use cookies, an Authorization header, custom headers, and ordinary CORS rules.
Browser EventSource uses GET, supports cookies according to its credentials mode, and can enable cross-origin credentials with withCredentials. It does not expose a general way to add arbitrary authorization headers or a request body.
The browser WebSocket constructor similarly does not accept arbitrary HTTP headers. A WebSocket server can authenticate cookies during the handshake or authenticate through application messages after opening. It must validate the browser's Origin because WebSocket is not governed by ordinary CORS response processing.
Regardless of transport:
The transport changes how credentials arrive, not whether access control is required.
All three mechanisms need a way to route a published event to interested clients.
With long polling, waiting requests may be distributed across many servers. A shared event log, database, or notification layer wakes the correct waiters. A cursor lets a replacement request reach a different server safely.
With SSE and WebSocket, a connection registry maps subscribers to the server processes or gateways that own their live connections. A shared publish-subscribe layer distributes each update to those owners.
During deployment:
EventSource reconnects.Reconnects should be spread with jitter or controlled retry delays. Releasing a large fleet of clients simultaneously creates a reconnect storm that can overload authentication, load balancers, and the new application instances before normal traffic resumes.
Operational metrics should distinguish:
HTTP request rate alone badly underrepresents SSE and WebSocket load, while connection count alone hides long-poll churn.
A client starts a job and waits for one result. Long polling is often sufficient: hold the request until the job completes or the poll times out, then retry with the job ID.
If one page tracks many jobs and receives frequent progress updates, one SSE stream can consolidate them more efficiently.
Notifications primarily flow from the server and are naturally represented as text records. SSE provides named events, automatic reconnect, and event IDs. Long polling remains a reasonable fallback where streaming responses are buffered or unsupported.
The server continuously emits metrics, status changes, or log lines. SSE closely matches the direction and can use normal HTTP infrastructure. Replay cursors help a dashboard recover after a brief network change.
Chat can be built with client POST requests and SSE delivery. That design is attractive when messages from the client are relatively infrequent and ordinary HTTP command semantics are useful.
When the experience includes frequent messages in both directions, typing state, presence, read receipts, and interactive session state, WebSocket usually provides a cleaner single-channel model.
Both endpoints send frequent, latency-sensitive operations. WebSocket is usually the natural choice because it provides a persistent full-duplex channel with low per-message framing overhead.
When compatibility with conservative HTTP infrastructure is the dominant requirement, long polling is often the safest starting point because it uses complete HTTP responses. Its timeout and load costs still need to be measured.
This flow is a starting point, not a substitute for testing. Prototype through the actual CDN, gateway, load balancer, authentication layer, and client networks. Measure delivery latency, resource use, reconnect behavior, and slow-client handling under realistic concurrency.
Long polling is not frequent short polling. The server holds the request until an event or timeout rather than immediately returning an empty response.
Long polling does not eliminate concurrent connections or requests. Each waiting client still consumes server and transport state.
A long poll does not require a new TCP handshake every time. HTTP can reuse or multiplex the underlying connection even though each response and request is a distinct HTTP exchange.
SSE is not automatically slower than WebSocket. Both can deliver an event promptly over an established path; workload and buffering determine observed latency.
WebSocket is not always more scalable. It reduces repeated request framing, but every live connection still consumes memory, routing state, and fan-out work.
SSE is not limited to one browser event name. Its event field supports named event types.
SSE cannot carry native binary messages. Encoding binary as text adds size and processing cost.
Full-duplex is not useful when only one side has something to say. Extra capability can introduce unnecessary connection and application complexity.
Automatic reconnection is not reliable replay. SSE reconnects automatically, but the server must retain events and interpret Last-Event-ID.
Persistent delivery does not mean exactly-once delivery. Every option needs explicit gap, duplicate, and ambiguous-failure handling.
Infrastructure support cannot be assumed from a local test. Timeouts, buffering, upgrades, and connection limits appear along the complete production path.
Protocol selection does not replace message design. IDs, versions, schemas, authorization, and error semantics remain application concerns.
Long polling, SSE, and WebSocket support low-latency updates with different communication models. Long polling returns one HTTP response per event or timeout, fitting infrequent updates and restrictive infrastructure. SSE keeps one response open for server-to-client text events with browser-managed reconnection and event IDs. WebSocket provides a full-duplex framed channel for frequent bidirectional or binary messages.
Long polling repeats request processing, while SSE and WebSocket retain connection state. None guarantees replay, deduplication, or exactly-once effects without application support. Production choices must also account for buffering, timeouts, backpressure, security, client count, and reconnect storms.
Choose long polling for compatibility, SSE for one-way text streams, and WebSocket only when a true two-way channel is needed.
5 quizzes