WebSockets in Chrome Extension Service Workers: Complete Guide (2026)
WebSockets in Chrome Extension Service Workers
WebSockets enable real-time, bidirectional communication between your extension and a server — essential for chat tools, live dashboards, collaborative features, and push-based updates. Since Chrome 116, service workers properly support WebSocket connections with a keepalive mechanism.
The Service Worker Challenge
Chrome terminates idle service workers after approximately 30 seconds of inactivity. Before Chrome 116, this killed any active WebSocket connections. The solution: send periodic keepalive messages to prevent the idle timeout.
Requirements: Chrome 116+ — add this to your manifest:
{
"manifest_version": 3,
"minimum_chrome_version": "116",
"permissions": [],
"background": {
"service_worker": "service-worker.js"
}
}Basic WebSocket Implementation
let ws = null;
let keepAliveInterval = null;
function connect() {
ws = new WebSocket('wss://your-server.com/ws');
ws.onopen = () => {
console.log('WebSocket connected');
startKeepAlive();
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
handleMessage(data);
};
ws.onclose = (event) => {
console.log(`WebSocket closed: ${event.code} ${event.reason}`);
stopKeepAlive();
ws = null;
scheduleReconnect();
};
ws.onerror = (error) => {
console.error('WebSocket error:', error);
};
}
function startKeepAlive() {
keepAliveInterval = setInterval(() => {
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }));
} else {
stopKeepAlive();
}
}, 20 * 1000); // Every 20 seconds — within the 30s idle timeout
}
function stopKeepAlive() {
if (keepAliveInterval) {
clearInterval(keepAliveInterval);
keepAliveInterval = null;
}
}
function disconnect() {
stopKeepAlive();
if (ws) {
ws.close(1000, 'Extension disconnecting');
ws = null;
}
}Why 20 seconds? The service worker idle timeout is 30 seconds. Sending a ping every 20 seconds provides a safe margin to keep the worker alive.
Reconnection with Exponential Backoff
Network interruptions happen. Implement automatic reconnection:
let reconnectAttempts = 0;
const MAX_RECONNECT_ATTEMPTS = 10;
const BASE_DELAY = 1000;
function scheduleReconnect() {
if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
console.error('Max reconnection attempts reached');
return;
}
const delay = Math.min(BASE_DELAY * Math.pow(2, reconnectAttempts), 60000);
const jitter = delay * 0.1 * Math.random();
setTimeout(() => {
reconnectAttempts++;
console.log(`Reconnecting (attempt ${reconnectAttempts})...`);
connect();
}, delay + jitter);
}
// Reset counter on successful connection
function onConnectionEstablished() {
reconnectAttempts = 0;
}Sending and Receiving Messages
Structured Message Protocol
function send(type, payload) {
if (!ws || ws.readyState !== WebSocket.OPEN) {
console.warn('WebSocket not connected, queuing message');
messageQueue.push({ type, payload });
return;
}
ws.send(JSON.stringify({ type, payload, timestamp: Date.now() }));
}
const messageQueue = [];
function flushQueue() {
while (messageQueue.length > 0 && ws?.readyState === WebSocket.OPEN) {
const msg = messageQueue.shift();
ws.send(JSON.stringify(msg));
}
}
function handleMessage(data) {
switch (data.type) {
case 'notification':
chrome.notifications.create({
type: 'basic',
iconUrl: 'icons/icon-128.png',
title: data.payload.title,
message: data.payload.body
});
break;
case 'badge-update':
chrome.action.setBadgeText({ text: String(data.payload.count) });
break;
case 'pong':
// Keepalive acknowledged
break;
default:
// Forward to popup/sidepanel if open
chrome.runtime.sendMessage(data).catch(() => {});
}
}Forwarding Messages to Popup/Side Panel
// service-worker.js
const connectedPorts = new Set();
chrome.runtime.onConnect.addListener((port) => {
connectedPorts.add(port);
port.onDisconnect.addListener(() => connectedPorts.delete(port));
});
function broadcastToUI(data) {
for (const port of connectedPorts) {
port.postMessage(data);
}
}
// In handleMessage, replace chrome.runtime.sendMessage with:
// broadcastToUI(data);// popup.js
const port = chrome.runtime.connect({ name: 'popup' });
port.onMessage.addListener((message) => {
updateUI(message);
});Connection State Management
function getConnectionState() {
if (!ws) return 'disconnected';
switch (ws.readyState) {
case WebSocket.CONNECTING: return 'connecting';
case WebSocket.OPEN: return 'connected';
case WebSocket.CLOSING: return 'closing';
case WebSocket.CLOSED: return 'disconnected';
}
}
// Expose state to popup
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'getStatus') {
sendResponse({
state: getConnectionState(),
reconnectAttempts,
queuedMessages: messageQueue.length
});
return true;
}
});When to Use WebSockets vs. Other Options
| Feature | WebSockets | Web Push | chrome.gcm |
|---|---|---|---|
| Direction | Bidirectional | Server → Client | Server → Client |
| Keeps worker alive | Yes (with keepalive) | No (wakes on message) | No (wakes on message) |
| Battery impact | Higher | Lower | Lower |
| Latency | Lowest | Medium | Medium |
| Best for | Live data, chat, collaboration | Notifications, updates | Legacy support |
Use WebSockets when you need frequent, bidirectional communication. For infrequent server-to-client updates, prefer Web Push — it’s more battery-friendly and wakes the service worker automatically.
What’s Next
WebSockets give your extension real-time capabilities that set it apart. Implement the keepalive pattern, add reconnection logic, and always provide fallback behavior for offline scenarios.
Grow your real-time extension with ExtensionBooster’s developer tools.
Share this article
Build better extensions with free tools
Icon generator, MV3 converter, review exporter, and more — no signup needed.
Related Articles
I Built the Same Chrome Extension With 5 Different Frameworks. Here's What Actually Happened.
WXT vs Plasmo vs CRXJS vs Extension.js vs Bedframe. Real benchmarks, honest opinions, and the framework with 12K stars that's quietly dying.
5 Best Email Marketing Services to Grow Your Chrome Extension (2026)
Compare the top email marketing platforms for SaaS and Chrome extension developers. MailerLite, Mailchimp, Brevo, ActiveCampaign, and Drip reviewed.
15 Best Practices to Build a Browser Extension That Users Love (2026 Guide)
Master browser extension development in 2026. Manifest V3, security, performance, and UX best practices to build extensions users love.