⚠️ Malware / Trojaner / Viren9 Proofpoint alternatives. Pros & cons of the leading options(24.08.2026 um 11:27 Uhr)
⚠️ Malware / Trojaner / VirenWhat the DfE’s cyber security update means for multi-academy trusts(24.08.2026 um 19:13 Uhr)
⚠️ Malware / Trojaner / VirenBuilding a ransomware decision tree before the call comes in(11.09.2026 um 07:30 Uhr)
🕵️ SicherheitslückenAutomox Mitigation Worklets cut endpoint exposure to unpatchable flaws(11.09.2026 um 09:48 Uhr)
⚠️ Malware / Trojaner / VirenFake Codex Download Uses Google Sites to Deliver macOS Malware(24.08.2026 um 17:00 Uhr)
⚠️ Malware / Trojaner / VirenFake Minecraft Clients Deliver WeedHack Malware Despite Infrastructure Takedown(25.08.2026 um 12:30 Uhr)
🕵️ SicherheitslückenFour in Five AI Tools Run with No IT Oversight, New Research Finds(26.08.2026 um 15:00 Uhr)
⚠️ Malware / Trojaner / VirenTortoiseshell Expands Malware Toolset With New Backdoor, SSH Tunnel(26.08.2026 um 16:30 Uhr)
⚠️ Malware / Trojaner / Viren9 Proofpoint alternatives. Pros & cons of the leading options(24.08.2026 um 11:27 Uhr)
⚠️ Malware / Trojaner / VirenWhat the DfE’s cyber security update means for multi-academy trusts(24.08.2026 um 19:13 Uhr)
⚠️ Malware / Trojaner / VirenBuilding a ransomware decision tree before the call comes in(11.09.2026 um 07:30 Uhr)
🕵️ SicherheitslückenAutomox Mitigation Worklets cut endpoint exposure to unpatchable flaws(11.09.2026 um 09:48 Uhr)
⚠️ Malware / Trojaner / VirenFake Codex Download Uses Google Sites to Deliver macOS Malware(24.08.2026 um 17:00 Uhr)
⚠️ Malware / Trojaner / VirenFake Minecraft Clients Deliver WeedHack Malware Despite Infrastructure Takedown(25.08.2026 um 12:30 Uhr)
🕵️ SicherheitslückenFour in Five AI Tools Run with No IT Oversight, New Research Finds(26.08.2026 um 15:00 Uhr)
⚠️ Malware / Trojaner / VirenTortoiseshell Expands Malware Toolset With New Backdoor, SSH Tunnel(26.08.2026 um 16:30 Uhr)

🔧 Programmierung 🕛 vor 1 Monat 8 Min Lesezeit
0

Making a WebSocket survive Chrome's Manifest V3

↗ Quelle (dev.to)
🗣️ Stimme:
📑 Inhaltsübersicht

The bug reports all sounded the same. "It disconnected again." No error, no warning, nothing on screen to explain it. Someone would open a GitHub issue, start a planning-poker vote, then stop to actually read the thing they were estimating. Thirty seconds later they'd look back and the room had quietly gone still. Other people's votes weren't showing up, and their own weren't reaching the room. It had simply stopped, without a word.



If you've ever built anything real on Chrome's Manifest V3, you can probably guess where this is going. The socket didn't drop because the network hiccupped. It dropped because Chrome reached over and killed the process it was living in.



This is the story of that bug, and what it actually takes to keep a WebSocket alive inside a Manifest V3 extension.




TL;DR: Manifest V3 runs your background code in a service worker that Chrome terminates after ~30s idle. If your WebSocket lives there, it dies with it, silently. The fix is three parts: a heartbeat so extension activity keeps the worker awake, auto-reconnect that pulls a fresh snapshot so drops are invisible, and a try/catch around every port message because the worker can die between your null-check and the call.







Why the socket doesn't live where you'd expect



First, some context on how the extension is wired, because it explains why this bug was even possible.



It puts a button on GitHub issues and boards. Click it, and a live planning-poker room opens right there in the page. Votes flow over a WebSocket to a Cloudflare . The idea is efficiency: don't keep code resident that isn't doing anything.



But a WebSocket is doing something. It's holding a connection open. The trouble is that holding a connection open doesn't, by itself, count as activity. So Chrome looks at your idle service worker, decides it's dead weight, and shuts it down. Your socket goes with it, silently, with no close frame the user ever sees.



That's the disconnect. Not a network problem. A lifecycle problem. The fix has three parts.






Part one: a heartbeat to stay awake



The first job is keeping the service worker alive while a room is open. And the rule that saves you is this: extension activity resets the idle timer. Messaging traffic counts. WebSocket traffic counts.



So the extension sends a heartbeat. Every 20 seconds, the content script posts a small message over the port, the background answers by sending a ping frame on the socket, and that traffic keeps the worker's idle timer from ever reaching zero.




CODE
// Keep the port busy roughly twice per MV3 idle window (~30s).
const HEARTBEAT_MS = 20_000;
this.heartbeat = setInterval(() => this.post({ __ping: true }), HEARTBEAT_MS);






Twenty seconds, not twenty-nine, on purpose. The idle window is around 30 seconds, so you want to poke it at least twice per window and leave slack for a slow frame. Cutting it close is how you end up debugging the same disconnect all over again.



Now, you might worry about the cost of pinging a server every 20 seconds for every open room. Here's the nice part. The room lives in a Durable Object that hibernates when nothing's happening, and Cloudflare lets you register an automatic response that answers a ping without ever waking it:




CODE
this.ctx.setWebSocketAutoResponse(new WebSocketRequestResponsePair("ping", "pong"));






So the heartbeat keeps the extension's service worker awake, but the server answers each ping with a pong from the runtime itself. The Durable Object stays asleep. You get the keepalive you need without paying to wake the room thousands of times a day.






Part two: reconnect like nothing happened



A heartbeat reduces disconnects. It doesn't eliminate them. Laptops sleep, networks flap, and Chrome will still occasionally win the race and tear the worker down anyway. So the second job is making any drop recover on its own.



When the socket dies, the extension reconnects with exponential backoff: wait a second, try again, and double the wait each time up to a 15-second ceiling.




CODE
const delay = this.reconnectDelay;
this.reconnectDelay = Math.min(this.reconnectDelay * 2, 15_000);
this.reconnectTimer = setTimeout(() => this.open(), delay);






The part that makes this feel seamless isn't the backoff, though. It's what happens on the other end. When a client connects to a room, the Durable Object's first move is to send a complete snapshot: the current ticket, every participant, who's voted, the whole state of the table. A reconnect gets the same snapshot a fresh connection does. So the client doesn't have to remember where it was or replay anything. It asks again and gets handed the current truth.



Which means a recovered drop is invisible. The socket blinks out, reconnects a second later, the snapshot repaints the room, and the person who stepped away to read a ticket never knows a thing happened.



Not every drop should retry, of course. If the room was deleted, the server closes with a specific code, and the client treats that as final. And after eight failed attempts in a row it gives up and asks the user to reload, because something like a revoked token will never succeed and looping forever helps nobody. It's tempting to reach for "retry everything" or "retry nothing," but both are wrong. Retry the transient stuff, quit on the terminal stuff.






Part three: the error that only shows up in production



Here's the one that cost me an afternoon.



Once the heartbeat and reconnect were in, everything worked on my machine. Then the console started filling with this:




CODE
Uncaught Error: Attempting to use a disconnected port object






The port to the background had gone dead, and I was still trying to post to it. My first instinct was a guard: check that the port exists before using it. I already had that check. It didn't help.



It didn't help because of when Manifest V3 kills the worker. It can happen at any moment, including the microscopic gap between checking that your port is alive and actually calling postMessage on it. You check, the check passes, Chrome kills the worker, you post, it throws. A time-of-check-to-time-of-use race, courtesy of a runtime that can pull the rug whenever it likes.



You can't check your way out of a race like that. You have to catch it.




CODE
post(msg) {
if (!this.port) return;
try {
this.port.postMessage(msg);
} catch {
this.port = null; // the worker died between the check and the call
}
}






Every send goes through that. The null check handles the common case, and the try/catch handles the case where the world changed underneath you mid-call. Once every postMessage was wrapped, the errors stopped. I've come to treat it as a rule for MV3: any time you touch a port, assume it might already be gone.






The payoff



Put the three together and the extension holds a live connection through everything a real workday throws at it. Idle stretches, sleeping laptops, flaky wifi, and Chrome's own eagerness to reclaim the service worker. The heartbeat keeps it awake, the reconnect makes drops invisible, and the guards keep a lifecycle race from spamming the console.



None of this is exotic. It's the kind of plumbing that never shows up in a demo and only reveals itself after a socket has been open for a few real hours with real people on it. But that's exactly the gap that matters: the difference between something that works when you're testing it and something that works when your team is leaning on it mid-sprint.






I build and .

Vollständiger Original-Bericht
Ausführliche Details, Code-Beispiele & Hersteller-Stellungnahme auf dev.to.
↗ Original-Artikel auf dev.to lesen
Wie bewertest du diesen Beitrag?
1 Klick Feedback
Teilen mit Netzwerk & Team:

Community-Analysen & Experten-Meinungen 0

Verfasse deine eigene Analyse, teile Workarounds oder diskutiere diesen Vorfall im Blog.
Noch keine Community-Analyse verfasst. Markiere einen Textabschnitt oder klicke oben auf Eigene Analyse verfassen“!
Community Pulse: Relevanz-Einschätzung
1 Klick Experten-Votum
🔴 Akute Relevanz 0%
🟡 In Evaluierung 0%
🟢 Keine Auswirkung 0%
Spannende Innovation 0%
Verwandte Story-Cluster & Quellen (Vektor-KI)
Port 8095 Engine
1 Quelle
9 Proofpoint alternatives. Pros & cons of the leading options
1 Quelle
What the DfE’s cyber security update means for multi-academy trusts
1 Quelle
Coffee with the Council Podcast: Celebrating 20 Years of Securing Payment Data
Ähnliche Beiträge
🔍 Verwandte News

Auch interessante Nachrichten Making a WebSocket survive Chrome's Manifest V3

Thematisch verwandte Begriffe: Making, WebSocket, survive, Chromes · 6 Treffer

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...

Laden...

Beiträge werden geladen ...

Laden...

Videos werden geladen ...