Chrome Killed MV2. Your Extension Needs This
uBlock Origin is gone from the Chrome Web Store. If you still ship an MV2 extension, here's what breaks and how to fix it.
Google has purged Manifest V2 extensions from the Chrome Web Store — uBlock Origin included. The highest-profile casualty of a migration years in the making, and it finally landed.
If your extension still says "manifest_version": 2, your listing is gone. No new users, no updates through the store. And the fix isn't flipping a number: MV3 breaks three things extension code almost always leans on.
Why this matters
Chrome is where most of your extension's users live. An MV2 extension is now frozen: existing installs may keep running, but you can't ship fixes and search returns nothing. The MV3 rewrite everyone deferred "until the deadline" is due yesterday. The migration is mechanical once you know the three breaking changes — it's the not-knowing that turns it into a month-long slog.
How it works
Background pages become service workers
The persistent background page is dead. MV3 gives you an event-driven service worker that Chrome kills after ~30 seconds of idleness and resurrects on demand. Two rules keep you sane: register every event listener synchronously at the top level (listeners attached inside async callbacks get missed when the worker wakes), and move all state into chrome.storage — memory does not survive termination.
Blocking webRequest becomes declarativeNetRequest
The observational webRequest API survives; the blocking variant is gone. In its place: declarativeNetRequest, where you hand the browser a ruleset and Chrome's network stack enforces it without ever calling your JavaScript. You add, remove, or swap rules at runtime with updateDynamicRules. It's faster than blocking-in-JS ever was, but you can only express rule-shaped logic: match a URL pattern, then block, redirect, or rewrite headers.
No more remote code
MV3's CSP bans eval and remotely hosted scripts. Everything executes from the bundled package. If your extension loads config-driven logic from your server, that pattern is finished — ship data, not code.
Where this helps
- Ad blockers: uBlock Origin's engine injects arbitrary per-site scriptlets, which DNR cannot express. That gap is why uBO is gone while the DNR rewrite, uBlock Origin Lite, survives with a reduced filter vocabulary. Clever per-request logic gets the same squeeze.
- Internal enterprise extensions: the
ExtensionManifestV2Availabilitypolicy keeps MV2 running on managed fleets. A bridge, not a home — you'd self-host the CRX via policy anyway since the store listing is gone. - Long-lived background work: pollers, websocket keepalives, token refresh timers must move to
chrome.alarmsplus persisted state, or worker shutdown eats them. - Header surgery: DNR's
modifyHeadersaction covers most request and response rewriting without intercepting traffic.
Watch out
Dynamic rules are capped — the documented ceiling is 30,000. Big filter lists need static rulesets you enable and disable, not one giant dynamic pile. Lifecycle bugs only appear under termination, so kill the worker manually from chrome://extensions while testing. And Mozilla still accepts MV2, so cross-browser means two manifest flavors or a build-time transform — one manifest.json won't travel.
Try it yourself
// MV3: block trackers without touching webRequest
// manifest.json: "manifest_version": 3 and
// "permissions": ["declarativeNetRequest"]
chrome.declarativeNetRequest.updateDynamicRules({
addRules: [{
id: 1,
priority: 1,
condition: {
urlFilter: "||doubleclick.net^",
resourceTypes: ["script", "sub_frame", "xmlhttprequest"]
},
action: { type: "block" }
}],
removeRuleIds: [1]
});
TL;DR
- What changed: MV2 extensions, uBlock Origin included, are gone from the Chrome Web Store.
- Why it matters: MV2 can't reach new users or ship updates; MV3 means service workers, declarativeNetRequest, and no remote code.
- Try today: bump to manifest_version 3, port one blocking rule to
updateDynamicRules, and replace onesetIntervalwithchrome.alarms.