Long Polling vs SSE vs WebSocket
IntermediateThree patterns for pushing real-time data from server to client — long polling (HTTP hack), Server-Sent Events (one-way HTTP stream), and WebSocket (full-duplex TCP channel) — each with distinct trade-offs in complexity, scalability, and browser support.
Overview
HTTP is request-response by design — clients ask, servers answer. But many applications need servers to push data (notifications, live feeds, chat, collaborative editing). Three techniques exist: Long Polling holds the HTTP request open until data arrives, then the client immediately re-requests. Server-Sent Events (SSE) is a persistent one-way HTTP stream for server→client pushes. WebSocket upgrades the HTTP connection to a full-duplex TCP channel for bidirectional real-time communication. Choosing the right pattern depends on the directionality, latency requirements, infrastructure constraints, and whether load balancers support the protocol.
Long Polling
Long polling is a workaround for HTTP's request-response model. The client sends a request and the server holds it open — responding only when data is available (or a timeout occurs). The client immediately sends the next request. This creates a pseudo-push mechanism with standard HTTP.
// Long polling — client side (JavaScript):
async function longPoll() {
while (true) {
try {
const res = await fetch('/api/notifications?lastId=' + lastEventId, {
signal: AbortSignal.timeout(30000) // 30s server timeout
})
const data = await res.json()
if (data.events?.length) {
processEvents(data.events)
lastEventId = data.events.at(-1).id
}
// Immediately reconnect
} catch (e) {
await sleep(1000) // backoff on error
}
}
}
// Server side (Spring Boot):
@GetMapping("/api/notifications")
public DeferredResult<List<Event>> poll(@RequestParam Long lastId) {
DeferredResult<List<Event>> result = new DeferredResult<>(28000L, List.of());
eventBus.subscribe(lastId, events -> result.setResult(events));
return result; // held open until event arrives or 28s timeout
}
// Characteristics:
// ✓ Works through all proxies and firewalls (plain HTTP)
// ✗ Double latency on each event (server must wait, client must reconnect)
// ✗ Server holds many threads/connections openServer-Sent Events (SSE)
SSE is a W3C standard for server-to-client streaming over a single HTTP connection. The server sends text/event-stream responses; the browser's EventSource API handles reconnection automatically. SSE is one-way (server→client only) and works through HTTP/2 and most proxies.
// SSE — server side (Spring Boot):
@GetMapping(value = "/api/feed", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter streamFeed(@RequestParam String userId) {
SseEmitter emitter = new SseEmitter(0L); // no timeout
sseRegistry.register(userId, emitter);
emitter.onCompletion(() -> sseRegistry.remove(userId));
emitter.onError(e -> sseRegistry.remove(userId));
return emitter;
}
// Push event to specific user:
emitter.send(SseEmitter.event()
.id(String.valueOf(eventId))
.name("notification")
.data(objectMapper.writeValueAsString(notification)));
// SSE event format (text/event-stream):
// id: 42
// event: notification
// data: {"type":"new_message","from":"Rahul","text":"Hey!"}
//
// (blank line = event delimiter)
// SSE — client side (browser):
const es = new EventSource('/api/feed?userId=123')
es.addEventListener('notification', e => {
const n = JSON.parse(e.data)
showNotification(n)
})
// EventSource auto-reconnects on disconnect, sends Last-Event-ID headerWebSocket
WebSocket upgrades an HTTP/1.1 connection to a persistent, full-duplex TCP channel. Both client and server can send messages at any time without waiting for a request. WebSocket uses its own framing protocol (not HTTP) once the upgrade handshake completes.
// WebSocket upgrade handshake (HTTP → WS):
// Client → Server:
GET /ws HTTP/1.1
Host: api.aicancode.org
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
// Server → Client:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
// ← from here, raw WebSocket frames (not HTTP)
// Spring Boot WebSocket (STOMP over WebSocket):
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").withSockJS(); // SockJS fallback
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue");
registry.setApplicationDestinationPrefixes("/app");
}
}
// Push to all subscribers of a topic:
messagingTemplate.convertAndSend("/topic/live-feed", event);Comparison & When to Use Each
The right choice depends on directionality, latency requirements, and infrastructure. SSE covers most server-push use cases with far less complexity than WebSocket. WebSocket is needed only for true bidirectional real-time communication.
// Comparison table:
// ┌──────────────┬──────────────┬──────────────────┬──────────────────────┐
// │ │ Long Polling │ SSE │ WebSocket │
// ├──────────────┼──────────────┼──────────────────┼──────────────────────┤
// │ Direction │ S→C (hacky) │ S→C only │ Full-duplex (S↔C) │
// │ Protocol │ HTTP │ HTTP │ WS (after upgrade) │
// │ Auto-reconnect│ Manual │ Yes (EventSource)│ Manual / library │
// │ Load balancer│ Any │ Any HTTP LB │ Needs sticky or WS- │
// │ │ │ │ aware proxy │
// │ Overhead │ High (HTTP │ Low (single conn)│ Lowest (raw frames) │
// │ │ headers each)│ │ │
// │ Complexity │ Low │ Low │ Medium–High │
// └──────────────┴──────────────┴──────────────────┴──────────────────────┘
// Choose Long Polling: legacy systems, must work through strict proxies,
// very infrequent events
// Choose SSE: dashboards, live feeds, notifications, progress bars
// (server pushes, client rarely sends)
// Choose WebSocket: chat, collaborative editing, multiplayer games,
// live trading — true bidirectional low-latencyKey Points to Remember
- 1Long polling holds the HTTP request open until data arrives, then the client immediately re-requests — works everywhere but doubles latency.
- 2SSE (EventSource) is a single persistent HTTP stream; the browser auto-reconnects and sends Last-Event-ID for resumability.
- 3WebSocket provides full-duplex communication over a single TCP connection — lowest overhead, but requires WS-aware proxies and manual reconnection logic.
- 4SSE is the right default for most server-push use cases (notifications, feeds); WebSocket only when the client also sends frequent messages.
- 5WebSocket load balancing requires sticky sessions or a shared pub/sub backend (Redis) because the persistent connection must reach the same server.
Interview Questions
Sign in to ask AriaWhat is the difference between SSE and WebSocket?
When would you choose long polling over SSE?
How do you scale WebSocket connections across multiple server instances?
How does the WebSocket upgrade handshake work?
Design a live notification system for 1 million concurrent users — which protocol would you use and why?
Ask Aria about Long Polling vs SSE vs WebSocket
Your personal AI tutor — ask anything about this concept
Revision Status
Personal Notes
Sign in to save personal notes for this topic.
Discussion
Sign in to join the discussion.