declarativeNetRequest rules replacing webRequest for a Manifest V3 ad blocker

cwspy.com · July 16, 2026 · 8 min read

Manifest V3 and Ad Blockers: What You Can (and Can't) Build Anymore

No category of extension has been hit harder by Manifest V3 than ad and content blockers. If you build a Manifest V3 ad blocker, or you are trying to understand why an ad blocker you use got weaker after an update, the cause is a single API change: webRequest, the blocking, runtime-inspection API blockers used to rely on, is gone for new extensions. Its replacement, declarativeNetRequest, works on fundamentally different terms.

Why declarativeNetRequest changes what a blocker can do

Under Manifest V2, a blocker could inspect every request as it happened and decide, in code, whether to block, redirect, or modify it — arbitrary logic, evaluated live. Under Manifest V3's declarativeNetRequest, you instead register a fixed set of rules ahead of time, and Chrome itself applies them. The rules are fast and privacy-friendlier (Chrome never has to hand your extension the full content of every request), but they are also capped and static.

Manifest V2 (webRequest)Manifest V3 (declarativeNetRequest)
Inspect and decide on every request at runtime, with arbitrary logicMatch requests against a pre-declared, static rule set
Effectively unlimited custom filter logicA capped number of rules per extension (dynamic + static combined)
Can rewrite response bodies and headers freelyLimited to a fixed set of declarative actions: block, redirect, modify headers
Rule updates apply instantly, computed on the flyRule sets are declared in the package or updated via the dynamic rules API, both bounded

The practical effect: extremely aggressive, highly customized blocking — the kind that adapts filter logic per-site in real time — is harder or impossible to replicate exactly. Most mainstream ad-blocking use cases (blocklist-based filtering, cosmetic hiding) still work well; what breaks is the long tail of advanced, dynamic filtering behavior.

The caps are concrete, not vague: at the time of writing, an extension gets 30,000 dynamic and session rules combined, plus a guaranteed minimum of 30,000 static rulesfrom its own rulesets, with additional static rules available from a shared cross-extension pool if capacity allows. Google occasionally raises these numbers, so check the current values in Chrome's own declarativeNetRequest rule-limit reference before you promise a specific blocklist size in your listing.

What still works well under Manifest V3

  • Standard blocklist filtering (EasyList-style rules) maps onto declarativeNetRequest rule sets reasonably directly.
  • Cosmetic filtering — hiding elements via CSS/DOM manipulation — is unaffected, since it never depended on webRequest.
  • Static, well-known tracker and ad-domain blocking works fine as pre-declared rules.

What is harder or lost

  • Blocking decisions that depend on response content inspected at runtime.
  • Extremely large, frequently updated custom rule sets bumping into rule caps.
  • Per-request logic that reacts to page context rather than a static URL pattern.
If your Manifest V3 ad blocker's value proposition is "we block more than the built-in filter," test that claim against declarativeNetRequest's real limits before you promise it in your listing. A description that overclaims what the extension can now do is a support-ticket and negative-review generator, not a growth lever.

What a declarativeNetRequest rule actually looks like

A static rule is a JSON object, not code — this is the shape every blocklist entry has to compile down to:

FieldPurpose
idUnique number identifying the rule
priorityWhich rule wins when more than one matches
action.type"block", "redirect", "allow", or "modifyHeaders"
condition.urlFilterThe URL pattern to match, e.g. "||ads.example.com^"
condition.resourceTypesWhich request types it applies to (script, image, xmlhttprequest, etc.)

A blocklist maintainer's job under Manifest V3 is largely converting existing filter syntax (EasyList-style ||domain.com^ patterns already map closely) into this rule shape and staying inside the caps above — not writing runtime logic anymore.

Building or migrating a blocker: what to check

  1. Audit your existing webRequest rules and count how many map cleanly onto declarativeNetRequest's static rule format.
  2. Check your rule count against the current dynamic and static rule limits — a blocklist that used to be unlimited may need to be pruned or prioritized.
  3. Move any content-based decisions (not just URL pattern matching) to what declarativeNetRequest's supported actions can express, or accept the capability is gone.
  4. Update your store listing description to reflect what the extension actually blocks now — do this before publishing the migrated build, not after users notice the gap.
  5. Ship, then watch reviews closely for the first two weeks; blocking-behavior regressions get reported fast and specifically by an engaged user base.

The competitive angle

Every blocker extension in a given category is going through the same migration on roughly the same timeline. That makes this a rare moment where the store's usual stability breaks: extensions that migrate cleanly and communicate the change well can pick up installs from users abandoning a competitor whose blocker got noticeably worse.