Beyond WebSocket: When Server-Sent Events (SSE) Is the Better Choice for Real-Time Push
This article compares polling, WebSocket, and Server-Sent Events (SSE) for server-to-client real-time messaging, detailing SSE's lightweight HTTP-based design, automatic reconnection, and simpler server requirements, then provides a complete Node.js/Express and vanilla HTML/JavaScript demo.
Three Approaches to Server Push
In daily development, servers often need to push data to clients — real-time dashboards, notification centers, chat features. The article outlines three classic solutions:
Polling — client repeatedly requests data via HTTP.
WebSocket — full-duplex ws/wss protocol.
SSE (Server-Sent Events) — unidirectional, HTTP-based long connection.
Why Polling Is the Last Resort
Polling creates an illusion of push but is actually client-initiated request/response cycles. Its drawbacks:
Every poll repeats the full HTTP connection handshake (TCP three-way handshake, TLS if HTTPS), wasting resources.
Client runs continuous requests from page load, consuming CPU/battery.
Browsers limit concurrent connections per domain (Chrome: 6); a long-running poll occupies one slot.
Long poll intervals delay data freshness; short intervals amplify overhead.
WebSocket: Powerful but Heavier
WebSocket enables bidirectional communication over a single TCP connection. Advantages: true duplex, low latency after handshake. Disadvantages:
Requires ws/wss protocol support on both client and server; not all HTTP infrastructure handles it.
More complex protocol (frames, masking, ping/pong) → heavier implementation.
No built-in reconnection; developers must implement heartbeat and retry logic.
Browser compatibility is broad (all modern browsers), but the article notes that SSE and WebSocket are now almost universally supported, making polling unnecessary except for legacy environments.
SSE: Lightweight, HTTP-Native, Auto-Reconnect
SSE is a unidirectional (server → client) long-lived HTTP connection. Key characteristics:
Runs over standard HTTP/HTTPS — existing servers, proxies, load balancers, and CDNs support it without changes.
Simpler protocol: text/event-stream with line-oriented fields (data:, event:, id:, retry:).
Built-in automatic reconnection with last-event-id resume capability.
Lower client-side resource usage than WebSocket.
Limitation: Internet Explorer does not support SSE; also unsupported in WeChat Mini Programs.
Official Comparison (WHATWG / MDN)
WebSocket = full-duplex, bidirectional; SSE = server-to-client only.
WebSocket needs new protocol support; SSE works on any HTTP server.
SSE is lighter and simpler; WebSocket is heavier and more complex.
SSE has native reconnection; WebSocket requires custom logic.
SSE allows custom event types via the event: field.
Choosing the Right Tool
Use SSE when the server pushes updates and the client does not need to send messages back over the same channel — e.g., real-time dashboards, notification feeds, stock tickers, progress updates.
Use WebSocket when bidirectional low-latency messaging is required — e.g., chat applications, collaborative editing, multiplayer games.
Avoid polling unless targeting extremely old browsers that support neither SSE nor WebSocket.
SSE Core API (EventSource)
const source = new EventSource(url);readyState constants: 0 (EventSource.CONNECTING) — connecting or reconnecting. 1 (EventSource.OPEN) — connection open, receiving data. 2 (EventSource.CLOSED) — connection closed, no retry.
Events: onopen — fired when connection establishes. onmessage — fired for each received message (data: field). onerror — fired on connection failure; browser auto-retries unless close() called.
Response format (server must send):
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
data: {"message": "Current time is 10:30:45"}Each message ends with a blank line (
). Custom event types use event: <name> before data:.
Complete Working Demo
Frontend (index.html)
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>SSE Demo</title></head><body> <ul id="ul"></ul></body><script>
// Check SSE support
let source = '';
if (!!window.EventSource) {
source = new EventSource('http://localhost:8088/sse/');
} else {
throw new Error("Current browser does not support SSE");
}
// Connection opened
source.onopen = function(event) {
console.log(source.readyState);
console.log("Long connection opened");
};
// Message received
source.onmessage = function(event) {
console.log(JSON.parse(event.data));
console.log("Received SSE message");
let li = document.createElement("li");
li.innerHTML = String(JSON.parse(event.data).message);
document.getElementById("ul").appendChild(li);
};
// Error / disconnect
source.onerror = function(event) {
console.log(source.readyState);
console.log("Long connection interrupted");
};
</script></html>Backend (index.js — Node.js + Express)
const express = require('express');
const app = express();
const port = 8088;
// CORS middleware
app.all("*", function(req, res, next) {
res.header("Access-Control-Allow-Origin", "*");
res.header("Access-Control-Allow-Headers", "Content-Type, Authorization, X-Requested-With");
res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
res.header("Access-Control-Allow-Credentials", true);
if (req.method === 'OPTIONS') {
res.sendStatus(200);
} else {
next();
}
});
app.get("/sse", (req, res) => {
res.set({
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
});
console.log("SSE connection established");
// Push current time every second
setInterval(() => {
console.log("Pushing data...");
const data = {
message: `Current time is ${new Date().toLocaleTimeString()}`
};
res.write(`data: ${JSON.stringify(data)}
`);
}, 1000);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});Running the Demo
Save frontend code as index.html and open in browser.
Create a folder, save backend code as index.js.
Run npm init -y, then npm i express, then node index.js.
Browser shows a growing list of timestamps pushed once per second.
Summary Checklist
SSE is lighter than WebSocket.
SSE uses HTTP/HTTPS; WebSocket uses ws/wss.
For server-to-client only: prefer SSE.
For bidirectional: choose WebSocket.
Both have excellent modern browser support.
Polling wastes client resources — avoid unless forced.
IE does not support SSE.
WeChat Mini Programs do not support SSE.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
