Allowlisting Sites with allow and allowAllRequests Rules

Build a site allowlist for an MV3 blocker: allow versus allowAllRequests, main_frame and sub_frame scoping, priorities that beat block rules, initiatorDomains, and syncing the list across devices.

Published October 2, 2026 Updated October 2, 2026 7 min read
Table of Contents

Every blocker needs an allowlist: “don’t block anything on my bank”, “this news site breaks with blocking on, pause it”, “always allow requests to this CDN”. With declarativeNetRequest the allowlist is just more rules — but which rule? An allow rule on the site’s domain lets the page’s own requests through while still blocking the third-party ads it loads. An allowAllRequests rule on the page’s frame lets everything that page loads through, which is what users usually mean. Mixing them up produces allowlists that half-work, and getting the priorities wrong produces allowlists that do not work at all. This guide builds one correctly. It belongs to declarativeNetRequest rules.

allow versus allowAllRequests

An allow rule matches individual requests: if a request matches it, that request is not blocked or redirected by lower-priority rules. Its condition describes the request — its URL, domain, resource type. An allowAllRequests rule matches frames: it applies only to main_frame and sub_frame requests, and when one matches, every request made by that frame and its descendant frames is allowed (subject to priority). Its condition describes the document being loaded. So “allow requests to cdn.example.com” is an allow rule with requestDomains: ["cdn.example.com"], while “turn off blocking on news.example” is an allowAllRequests rule with requestDomains: ["news.example"] and resourceTypes: ["main_frame"].

allow versus allowAllRequestsWhat each action matches, which resource types it accepts, what it allows, and the typical use for each.AspectallowallowAllRequestsMatchesA requestA frame's documentresourceTypesAnymain_frame, sub_frame onlyAllowsThat requestAll requests in the frame treeTypical useAllow a CDN or APIPause a whole site
allow is about one request; allowAllRequests is about everything a page loads.

Step-by-step: a working site allowlist

1. Choose a priority band above every block rule

1// rules/priorities.js
2export const PRIORITY = Object.freeze({
3  listBlock: 1,          // static filter lists
4  userBlock: 2,          // user-added blocks
5  listException: 3,      // exceptions from filter lists (@@ rules)
6  userAllow: 50,         // user's allowed resources
7  siteAllow: 100,        // user's allowlisted sites — must win over everything
8});

Execution context: a shared constants module. When rules of different priorities match the same request, the highest priority wins; actions only break ties at equal priority. A site allowlist must sit above every block rule, including any added later, so give it a wide margin. Recording the bands in one place stops a future rule from accidentally outranking the allowlist.

2. Allowlist a whole site with allowAllRequests

 1// sw.js
 2export async function allowSite(hostname) {
 3  const id = await ruleIdFor(`site:${hostname}`);
 4  await chrome.declarativeNetRequest.updateDynamicRules({
 5    removeRuleIds: [id],
 6    addRules: [{
 7      id,
 8      priority: PRIORITY.siteAllow,
 9      action: { type: "allowAllRequests" },
10      condition: { requestDomains: [hostname], resourceTypes: ["main_frame"] },
11    }],
12  });
13}

Execution context: the service worker. requestDomains matches the domain and its subdomains, so example.com covers www.example.com. Matching main_frame means the rule triggers when the user navigates to the site; from then on, every request the page makes — including to ad networks — is allowed. Removing the rule id first makes the call idempotent.

A page load on an allowlisted siteThe main-frame navigation to news.example matches the allowAllRequests rule; subsequent requests from that page to an ad network match a block rule at priority 1 but are allowed because the frame-level allow at priority 100 wins.TabDNR matcherNetworkmain_frame news.exampleallowAllRequests p100 ✓script ads.tracker.exampleblock p1 vs frame allow p100allowed
The frame-level decision is made once, at navigation, and applies to everything the page loads.

3. Cover embedded frames deliberately

1condition: { requestDomains: [hostname], resourceTypes: ["main_frame", "sub_frame"] }

Execution context: the same rule with sub_frame added. With sub_frame, the allowlisted site is also unblocked when it appears as an iframe on other sites — an embedded video player, for example. Whether users want that depends on the feature; for “pause on this site”, main_frame alone is usually correct, because the user is thinking about the site in the address bar.

4. Allow specific resources everywhere with allow

 1export async function allowResourceDomain(domain) {
 2  const id = await ruleIdFor(`res:${domain}`);
 3  await chrome.declarativeNetRequest.updateDynamicRules({
 4    removeRuleIds: [id],
 5    addRules: [{
 6      id,
 7      priority: PRIORITY.userAllow,
 8      action: { type: "allow" },
 9      condition: { requestDomains: [domain] },
10    }],
11  });
12}

Execution context: the service worker. This is the fix for “the extension blocks the comment widget on every site”: an allow for the widget’s domain lets its requests through wherever they appear, while blocking stays active for everything else. Narrow it with initiatorDomains if the allowance should apply only when the request comes from certain sites.

Which allow rule does this request need?Decision tree: if the user wants a whole site unblocked, use allowAllRequests on main_frame; if a resource is broken everywhere, use allow on its domain; if a resource is broken on one site, use allow with initiatorDomains.What does the user want?this site, entirelyallowAllRequestsmain_frame, p100Everything on the pageloads normallythis resource, everywhereallowrequestDomains, p50Only that domainblocking stays onthis resource, hereallow + initiatorDomainsp50Only on that sitenarrowest exception
Ask whether the user is thinking about a page or about a resource.

5. Keep the allowlist as data and sync it

1// storage is the source of truth; rules are derived from it
2export async function setAllowlist(hostnames) {
3  await chrome.storage.sync.set({ allowlist: [...new Set(hostnames)].sort() });
4}
5
6chrome.storage.onChanged.addListener(async (changes, area) => {
7  if (area !== "sync" || !changes.allowlist) return;
8  await rebuildAllowRules(changes.allowlist.newValue ?? []);
9});

Execution context: the service worker. Storing the hostnames in chrome.storage.sync makes the allowlist follow the user to other devices; deriving rules from it on every change — and on onInstalled — keeps rules and data consistent. Dynamic rules themselves do not sync. Keep each hostname short and the list small: sync has a 100 KB total quota.

6. Rebuild rules from the list atomically

 1async function rebuildAllowRules(hostnames) {
 2  const existing = (await chrome.declarativeNetRequest.getDynamicRules())
 3    .filter((r) => r.priority === PRIORITY.siteAllow);
 4  await chrome.declarativeNetRequest.updateDynamicRules({
 5    removeRuleIds: existing.map((r) => r.id),
 6    addRules: hostnames.map((h, i) => ({
 7      id: 500_000 + i,
 8      priority: PRIORITY.siteAllow,
 9      action: { type: "allowAllRequests" },
10      condition: { requestDomains: [h], resourceTypes: ["main_frame"] },
11    })),
12  });
13}

Execution context: the service worker. One updateDynamicRules call that removes the old set and adds the new set is applied atomically, so there is no moment where the allowlist is empty. Reserving an id range for allowlist rules keeps them from colliding with user block rules. For large allowlists, a single rule with many requestDomains is more economical than one rule per site.

7. Show allowlist state in the popup

The popup should say clearly whether blocking is active on the current site and offer one button to toggle it. Read the allowlist from storage (not from the rules) and the tab’s hostname from the active tab; after toggling, reload the tab so the frame-level decision is re-made with the new rules.

Common mistakes

  • Using allow to pause a site. Third-party requests from the page are still blocked.
  • Allowlist priority too low. Equal-priority block and allow rules resolve by action order, which is fragile; outrank decisively.
  • allowAllRequests with resource types other than frames. The rule is invalid; it only accepts main_frame and sub_frame.
  • Not reloading the tab. Frame-level decisions are made at navigation; changes apply on the next load.
  • Syncing rules instead of data. Dynamic rules are per profile; sync the hostnames and rebuild.

Cross-browser variation

  • Chrome / Edge: allow and allowAllRequests as described; priorities and action ordering per the DNR specification.
  • Firefox: supports both actions in DNR; Firefox also runs blocking webRequest listeners, which an allowlist built only in DNR does not affect — keep both mechanisms consistent if you use both.
  • Safari: supports both actions; test allowlisting with Safari’s own content blocking features disabled to avoid confusing interactions.

Verification

  1. Allowlist a site and reload it: requests that were blocked now succeed in DevTools’ Network panel.
  2. Visit another site and confirm blocking is still active there.
  3. Remove the site and reload: blocking resumes.
  4. Change the allowlist on a second synced device and confirm rules rebuild on the first after sync.

FAQ

Can I allowlist a single page rather than a site?

Yes — use urlFilter with the page’s path and resourceTypes: ["main_frame"] in an allowAllRequests rule.

Does allowAllRequests affect other extensions?

No. Each extension’s rules apply to its own decisions; another blocker evaluates its own rules independently.

Why is a request still blocked on an allowlisted site?

It may come from a frame that was not covered (a sub frame from another origin) or a rule with a higher priority. Use DNR feedback in an unpacked build to see which rule matched.

Other Core APIs & Cross-Browser Data Management Resources