Real-Time Communication on the Web: Polling, SSE, WebSockets, WebRTC, and WebTransport
“Realtime” is not one protocol. A live dashboard, multiplayer game, video call, notification bell, and collaborative editor have very different transport requirements.
The right question is not “Should we use WebSockets?” It is:
- who needs to send data to whom?
- how quickly?
- must every message arrive?
- must messages remain ordered?
- can data be recovered after reconnect?
- how much browser compatibility is required?
- how much connection infrastructure are you willing to operate?
Modern browsers provide several useful options: ordinary HTTP polling, Server-Sent Events (SSE), WebSockets, WebRTC, and increasingly WebTransport.
The transport decision in one table
| Transport | Direction | Good fit | Main trade-off |
|---|---|---|---|
| Polling | client request / server response | simple infrequent updates | repeated requests and delay |
| Long polling | client request held until update | compatibility without persistent socket API | request churn and awkward scaling |
| SSE | server to client | notifications, feeds, progress, dashboards | one-way application data |
| WebSocket | bidirectional | chat, collaboration, interactive realtime state | connection lifecycle + no built-in standard WebSocket backpressure |
| WebRTC | peer-to-peer media/data | voice, video, P2P data | signaling/NAT/media complexity |
| WebTransport | client/server streams + datagrams | advanced low-latency protocols | newer ecosystem and more complexity |
Do not choose the most sophisticated option. Choose the simplest transport whose semantics match the feature.
Polling is still a valid design
Polling means the client periodically requests current state:
GET /jobs/123/status
perhaps every few seconds.
Polling is a good fit when:
- updates are infrequent
- a few seconds of delay is acceptable
- the number of clients is modest
- the endpoint is cheap/cacheable
- infrastructure simplicity matters more than instant delivery
Examples include:
- export progress
- occasional deployment status
- admin dashboards refreshed every 30 seconds
- low-frequency background state
Polling is not “bad architecture.” Keeping thousands of mostly idle realtime connections can be more operational work than a bounded polling endpoint when the product does not require immediacy.
Add jitter to large polling fleets
If every browser polls exactly every 10 seconds from the moment a page loads, large cohorts can synchronize and create traffic spikes.
Use small randomized jitter around the interval:
base interval: 10 seconds
actual interval: slightly randomized
Also back off when:
- the tab is hidden
- the server returns errors
- the process is known to be slow
The polling frequency should follow the freshness requirement, not arbitrary impatience.
Long polling reduces empty responses
With long polling, the server keeps the HTTP request open until:
- new data exists
- a timeout expires
- the connection closes
The client processes the response and immediately opens another request.
This can provide lower latency than periodic polling while keeping ordinary HTTP semantics.
Long polling can be useful for legacy clients or environments where other realtime APIs are unavailable.
For modern browser applications that mainly need server-to-client events, SSE often gives a cleaner API.
SSE is excellent for server-to-client streams
Server-Sent Events use a persistent HTTP response with content type:
text/event-stream
In the browser, EventSource receives events from the server.
MDN documents SSE as a unidirectional transport: server to client.
That makes it a good fit for:
- live notifications
- deployment/build logs
- progress events
- stock/metric updates
- news/activity feeds
- server-generated AI/token streams when the application pattern fits
The browser can still send normal HTTP POST, PATCH, or other requests separately.
A feature does not need one bidirectional socket merely because users occasionally send commands back to the server.
SSE has simple event IDs for resume semantics
An SSE event can contain an ID:
id: 1042
event: order.updated
data: {"order_id":"o77"}
After reconnect, the browser can send the last event ID so the server can resume appropriately when the backend has retained/reconstructable events.
Do not confuse protocol reconnection with durable application history.
If missing an event would cause incorrect business state, keep the event/state in a durable source that can rebuild the gap.
WebSockets provide full two-way communication
The standard WebSocket API opens a persistent bidirectional connection between browser and server.
Once connected, both sides can send messages without creating a new HTTP request for each update.
WebSockets fit applications such as:
- chat
- collaborative editing
- realtime multiplayer state
- interactive dashboards with frequent client commands
- bidirectional control streams
- live presence systems
They are mature and widely supported in browsers.
The cost moves from request overhead to connection lifecycle management.
WebSocket does not define your application protocol
A WebSocket gives you frames and a connection. You still need to define concepts such as:
{
"type": "order.updated",
"id": "evt_1042",
"payload": {}
}
Your protocol should answer:
- message types
- IDs
- versions
- authorization scope
- acknowledgement requirements
- ordering guarantees
- error format
- reconnect/cursor behavior
Without a defined application protocol, a realtime system becomes a collection of ad-hoc JSON messages that are difficult to evolve safely.
Do not rely on a WebSocket for durability
If a connection drops, frames sent while the client was disconnected do not magically reappear later.
Important state should be recoverable from durable storage:
connect
send last known cursor
fetch/replay missing durable state
resume live stream
This principle matters for:
- chat messages
- notifications
- order state
- collaborative documents
Ephemeral events such as cursor position or typing indicators can be allowed to disappear.
The standard browser WebSocket API has no built-in backpressure
Current MDN documentation explicitly notes that the normal WebSocket interface does not provide backpressure.
If messages arrive faster than the application can process them, client-side buffering and CPU use can grow dangerously.
Your application still needs bounded behavior:
incoming durable updates too far behind
-> disconnect or stop realtime processing
-> recover from durable state
ephemeral updates too fast
-> sample/coalesce/drop
Servers need the same discipline for outbound per-connection queues.
Never let one slow browser accumulate an unlimited backlog in gateway memory.
WebSocketStream is not a safe universal production assumption
WebSocketStream combines WebSocket-style communication with the Streams API and automatic backpressure.
However, MDN currently marks it non-standard and notes limited browser-engine support.
Do not design a broadly compatible production web product around it without feature detection and a fallback.
For standard WebSocket applications, implement explicit queue limits instead.
WebTransport is the modern advanced client/server option
WebTransport provides a client-server transport over HTTP/3 with capabilities that WebSockets do not expose directly, including:
- multiple independent streams
- unidirectional streams
- reliable ordered stream delivery
- unreliable datagrams
- better stream-level backpressure primitives
The W3C published a WebTransport Candidate Recommendation Snapshot on 30 July 2026, and MDN marks WebTransport as newly available across the latest browser generation in 2026, while noting older-browser limitations.
This makes WebTransport interesting for advanced applications such as:
- low-latency game networking
- interactive media/control protocols
- apps that need a mix of reliable streams and disposable datagrams
- custom protocols where head-of-line blocking between unrelated messages matters
It is still more complex than WebSocket and should not replace a simple WebSocket merely because it is newer.
Reliable streams and disposable datagrams solve different problems
Imagine a multiplayer game.
These events should probably arrive reliably:
player joined
match ended
inventory changed
But position updates can become obsolete immediately:
position at t1
position at t2
position at t3
If t3 arrives, waiting for a lost t1 packet may be useless.
A transport that supports unreliable datagrams lets the protocol express that distinction.
WebSocket normally gives the application one reliable ordered byte/message stream, which is ideal for many products but not every low-latency protocol.
WebRTC is primarily for peer-to-peer communication
WebRTC is the browser platform for realtime peer media and data.
It supports:
- audio
- video
- screen sharing
RTCDataChannelfor arbitrary peer-to-peer data
WebRTC data channels are encrypted, and applications can configure delivery behavior for suitable workloads.
Use WebRTC when peers should exchange media or data directly rather than routing the full payload through your application server.
Typical use cases:
- video calls
- voice calls
- screen sharing
- peer-to-peer file/data transfer
- some multiplayer/game scenarios
WebRTC still needs signaling
WebRTC peers cannot establish a connection from nothing.
The application needs a signaling mechanism to exchange connection setup information.
That signaling can use:
- WebSocket
- HTTP
- SSE plus HTTP writes
- another application channel
WebRTC also needs ICE/STUN/TURN infrastructure to work through real-world NAT and firewall conditions.
A “peer-to-peer” product therefore still requires server infrastructure; it simply changes which data path travels directly between peers.
TURN capacity matters for media products
Not every WebRTC connection can establish a direct peer route.
When relaying through TURN is required, media traffic passes through your relay infrastructure.
That means video/voice capacity planning needs to include:
- relay bandwidth
- geographic placement
- egress cost
- connection limits
- failover
Do not estimate a WebRTC architecture assuming every call is always direct peer-to-peer.
Realtime delivery and event durability are separate layers
A robust architecture often looks like:
source of truth / durable event log
|
realtime gateway
|
connected clients
The gateway optimizes latency.
The source of truth makes reconnect/recovery possible.
This distinction lets you safely:
- restart gateway instances
- disconnect slow clients
- rebalance connections
- recover after mobile network changes
without losing business state.
Horizontal scaling requires cross-gateway fan-out
One gateway instance knows only about its own connected clients.
With several gateway instances:
client A -> gateway 1
client B -> gateway 2
client C -> gateway 3
an event produced on gateway 1 may need to reach users connected elsewhere.
Common architectures use a broker/pub-sub/event layer between gateway instances.
The broker's durability should match the event type:
- typing/presence can often be ephemeral
- chat/order events need durable recovery elsewhere
Do not make every ephemeral cursor movement a durable Kafka-style event unless the workload genuinely needs that cost.
Connection routing and sticky sessions are separate concerns
After a WebSocket/SSE connection is established, it remains attached to the selected server until it closes.
That does not necessarily mean all normal HTTP API requests from the user must be permanently sticky to the same backend.
Keep durable user state outside the connection process where possible so reconnecting to another gateway is safe.
A gateway restart should be a recoverable connection event, not a data-loss event.
Heartbeats detect dead connections
Networks can disappear without a clean close handshake.
Use protocol/library-level ping/pong or application heartbeat mechanisms appropriate to the stack so servers can clean up dead connections.
Do not use extremely aggressive heartbeat intervals without need; they consume bandwidth, battery, and server work.
Presence state should expire when heartbeats stop rather than remaining permanently “online.”
Authentication must account for long-lived sessions
Realtime connections can stay open much longer than normal HTTP requests.
At connection time:
- authenticate the session/token
- bind the connection to the authenticated identity
- authorize requested topics/rooms
During the connection lifecycle, decide what happens if:
- session is revoked
- user is disabled
- tenant membership changes
- role changes
High-risk authorization changes may need to actively terminate or revalidate live connections.
Do not put sensitive credentials in URL query strings casually
WebSocket clients and infrastructure often expose URLs in logs and diagnostics.
Prefer authentication mechanisms that minimize long-lived secrets in URLs.
If the browser/API constraints require a token during connection setup, keep it short-lived, scoped, and avoid logging it.
The exact mechanism depends on the client, gateway, cookie/CORS model, and infrastructure.
Order only what needs ordering
Global total ordering across an entire realtime system is expensive and usually unnecessary.
A chat product may need ordering inside one conversation.
A game may need sequence numbers per entity or match.
A notification feed may use one cursor per user.
Define the smallest ordering domain that satisfies the product.
This makes partitioning and horizontal scaling much easier.
Make events idempotent where retries are possible
A client can reconnect and replay a request. A broker can redeliver. A gateway can fail after processing but before acknowledging.
Give durable operations stable IDs and make consumers able to recognize repeats.
For example:
command_id
message_id
event_id
version
Exactly-once behavior is usually constructed from durable state and idempotency—not assumed from the transport.
Backpressure is a full-system property
It is not enough for the browser to be able to slow its reads.
Consider the full chain:
producer
-> broker
-> realtime gateway
-> network
-> browser
-> UI rendering
Each boundary needs limits.
Possible controls include:
- bounded queues
- rate limits
- coalescing newer state over obsolete older state
- dropping ephemeral events
- disconnecting lagging consumers
- load shedding
Without limits, a sudden event burst moves the backlog somewhere until memory or latency collapses.
Choose by product semantics
Use polling when
- updates are rare
- delay is acceptable
- simplicity wins
Use SSE when
- the browser mostly receives server events
- normal HTTP is fine for client commands
- automatic/simple event-stream semantics are desirable
Use WebSocket when
- frequent bidirectional messages are central
- broad compatibility matters
- you can operate persistent connections and explicit backpressure limits
Use WebRTC when
- audio/video or direct peer data is the core workload
- you can operate signaling and STUN/TURN infrastructure
Evaluate WebTransport when
- you need independent streams or unreliable datagrams
- latest-browser support fits your audience
- your protocol benefits enough to justify more complexity
Production checklist
Before shipping a realtime feature, answer:
- Is realtime truly necessary, or is polling enough?
- Is communication one-way or bidirectional?
- Which events are durable and which are disposable?
- How does a client recover after disconnect?
- What is the ordering domain?
- Which operations must be idempotent?
- What happens when a client is slow?
- How are connections authenticated and later revoked?
- How do events reach users connected to other gateway instances?
- Are heartbeats and presence bounded by TTL/leases?
- Does browser support match the chosen API?
- Can gateway restarts occur without losing business state?
Realtime architecture is not about choosing the protocol with the lowest theoretical latency. It is about choosing a transport whose directionality, reliability, recovery, browser support, and operational cost match the user experience you actually need.
References
BUILDING A SAAS PRODUCT?
Explore Lofingo’s end-to-end SaaS platforms, multi-tenant cloud architecture, and high-concurrency backend engineering.
EXPLORE SAAS SERVICES →