# How to Programmatically Add Dynamic Filter Rules in uBlock Origin

> Learn how to programmatically add dynamic filter rules in uBlock Origin. This guide explains using the Chromium declarativeNetRequest API for advanced filtering.

- Repository: [Raymond Hill/uBlock](https://github.com/gorhill/uBlock)
- Tags: how-to-guide
- Published: 2026-03-05

---

**You can programmatically add dynamic filter rules in uBlock Origin by invoking the Chromium `declarativeNetRequest` API directly, using `chrome.declarativeNetRequest.updateDynamicRules()` with rule objects that match the internal conversion logic found in [`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js) and [`platform/mv3/extension/js/ruleset-manager.js`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/ruleset-manager.js).**

uBlock Origin implements its dynamic filtering engine on top of the Chromium Manifest V3 **declarativeNetRequest** (DNR) API, storing user-created rules as textual representations in `µBlock.permanentFirewall` and converting them to DNR format during startup. Understanding this pipeline allows external scripts and companion extensions to manage network filtering programmatically using the same APIs that uBlock uses internally. This article examines the exact source code mechanisms in the gorhill/uBlock repository and provides practical implementations for adding, replacing, and removing dynamic rules.

## Understanding the Dynamic Filtering Architecture

When a user creates a rule in uBlock Origin's "Dynamic filtering" pane, the UI writes a textual representation (e.g., `example.com * 3p block`) into the dynamic-filtering string stored in `µBlock.permanentFirewall`. During extension startup, this string is parsed by **[`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js)** via `DynamicHostRuleFiltering.fromString` and converted into a set of DNR-compatible rule objects.

The bridge that pushes these rules to the browser lives in **[`platform/mv3/extension/js/ruleset-manager.js`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/ruleset-manager.js)**. Here, the `RulesetManager.updateDynamicRules()` method (lines 189-197) calls `chrome.declarativeNetRequest.updateDynamicRules()` with the generated rule set. The extension distinguishes between **session rules** (temporary, cleared on reload) and **permanent rules** (persisted in `chrome.storage.local`), with session updates utilizing `chrome.declarativeNetRequest.updateSessionRules()`.

## The Rule Conversion Pipeline

The conversion from uBlock's internal format to DNR rules involves three key stages:

- **Parsing**: `DynamicHostRuleFiltering.fromString()` (lines 994-998 in [`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js)) parses the textual representation into an internal bitmap structure.
- **Serialization**: `DynamicHostRuleFiltering.toArray()` (lines 998-1014) builds an array of human-readable rules, each transformed into a DNR rule object containing `id`, `priority`, `action`, and `condition` properties.
- **Application**: `RulesetManager.updateDynamicRules()` translates these objects into the final format required by the browser API.

When the UI removes a rule via the **X** button in the dynamic-filtering pane, [`src/js/logger-ui.js`](https://github.com/gorhill/uBlock/blob/main/src/js/logger-ui.js) (lines 777-805) triggers the messaging layer in [`src/js/messaging.js`](https://github.com/gorhill/uBlock/blob/main/src/js/messaging.js) (lines 1240-1250), which updates `µb.sessionFirewall` and reapplies dynamic rules using the same removal-then-addition pattern.

## Programmatically Managing Dynamic Rules

Any external script or companion extension with the `declarativeNetRequest` permission can manipulate dynamic rules by calling the same DNR APIs that uBlock Origin uses internally.

### Adding a New Dynamic Rule

To add a rule that blocks all third-party scripts from a specific domain, construct a DNR rule object with a unique numeric ID and appropriate conditions:

```js
// Add a rule that blocks all third-party scripts from example.com
chrome.declarativeNetRequest.updateDynamicRules({
  addRules: [{
    id: 1001,                         // Unique numeric ID (must not clash with existing rules)
    priority: 1,                      // Higher priority wins
    action: { type: "block" },
    condition: {
      urlFilter: "*://example.com/*", // Matches any scheme + host
      resourceTypes: ["script"],      // Only script resources
      requestDomains: ["*"],          // Any request domain → treated as third-party by uBlock
    }
  }],
  removeRuleIds: []                  // No removals in this call
}, () => {
  if (chrome.runtime.lastError) {
    console.error(chrome.runtime.lastError);
  } else {
    console.log('Dynamic rule added.');
  }
});

```

This corresponds to uBlock's internal flow: [`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js) parses the textual rule, then [`platform/mv3/extension/js/ruleset-manager.js`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/ruleset-manager.js) calls `updateDynamicRules`.

### Replacing an Existing Rule

To modify an existing rule, you must remove the old ID and add the new configuration in a single call:

```js
// Replace rule 1001 with a higher-priority allow rule
chrome.declarativeNetRequest.updateDynamicRules({
  removeRuleIds: [1001],
  addRules: [{
    id: 1001,
    priority: 10,                     // Higher priority overrides previous block
    action: { type: "allow" },
    condition: {
      urlFilter: "*://example.com/*",
      resourceTypes: ["script"]
    }
  }]
});

```

The removal-then-addition pattern mirrors how uBlock updates its `dynamicFilteringString`, as seen in [`src/js/messaging.js`](https://github.com/gorhill/uBlock/blob/main/src/js/messaging.js) where the code calls `µb.sessionFirewall.fromString()` followed by `µb.applyDynamicRules()`.

### Removing a Dynamic Rule

To delete a rule permanently, specify its ID in the `removeRuleIds` array:

```js
// Remove the rule whose ID is 1001
chrome.declarativeNetRequest.updateDynamicRules({
  removeRuleIds: [1001],
  addRules: []                      // No new rules to add
});

```

This mimics the behavior triggered when users click the remove button in the logger UI ([`src/js/logger-ui.js`](https://github.com/gorhill/uBlock/blob/main/src/js/logger-ui.js) handling `onDynamicRuleRemove`).

### Working with Session-Only Rules

For temporary rules that clear when the browser restarts, use `updateSessionRules()` instead:

```js
// Session-only rule: block all 3p images from any site while the tab is alive
chrome.declarativeNetRequest.updateSessionRules({
  addRules: [{
    id: 2001,
    priority: 1,
    action: { type: "block" },
    condition: {
      urlFilter: "*",
      resourceTypes: ["image"],
      // No requestDomains → treated as third-party by uBlock's logic
    }
  }],
  removeRuleIds: []
});

```

uBlock calls `chrome.declarativeNetRequest.updateSessionRules()` when users add temporary rules via the "Temporary" toggle in the interface.

## Key Source Files and Implementation Details

| File | Role | Critical Lines |
|------|------|----------------|
| [`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js) | Parses textual representation (`fromString`) and builds internal bitmap; provides `toArray()` which yields the rule list fed to DNR | 998-1014 (`toArray`), 994-998 (`fromString`) |
| [`platform/mv3/extension/js/ruleset-manager.js`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/ruleset-manager.js) | Translates uBlock's internal rule objects into DNR rule objects and calls `chrome.declarativeNetRequest.updateDynamicRules` / `updateSessionRules` | 189-197 (`updateDynamicRules`) |
| [`src/js/messaging.js`](https://github.com/gorhill/uBlock/blob/main/src/js/messaging.js) | Receives messages from the UI to add/remove dynamic rules; updates `µb.permanentFirewall` and triggers rule recomputation | 1240-1250 (rule-add handling) |
| [`src/js/logger-ui.js`](https://github.com/gorhill/uBlock/blob/main/src/js/logger-ui.js) | UI component that lets users edit dynamic rules; ultimately invokes the messaging layer | 777-805 (dynamic rule UI actions) |
| [`platform/mv3/extension/js/manifest.json`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/manifest.json) | Declares the required `"declarativeNetRequest"` permission necessary for any external manipulation | `"permissions": ["declarativeNetRequest"]` |

## Summary

- **uBlock Origin** converts textual dynamic filter rules into DNR-compatible objects through [`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js) before applying them via [`platform/mv3/extension/js/ruleset-manager.js`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/ruleset-manager.js).
- **External scripts** can bypass uBlock's UI layer and call `chrome.declarativeNetRequest.updateDynamicRules()` directly to add permanent rules, using unique numeric IDs and proper priority values.
- **Session rules** provide temporary filtering that persists only for the current browser session, managed through `chrome.declarativeNetRequest.updateSessionRules()`.
- **Rule modifications** require the removal-then-addition pattern to ensure atomic updates and prevent ID conflicts.
- All programmatic manipulation requires the `declarativeNetRequest` permission declared in [`manifest.json`](https://github.com/gorhill/uBlock/blob/main/manifest.json).

## Frequently Asked Questions

### Can external extensions modify uBlock Origin's dynamic rules directly?

External extensions cannot directly modify uBlock Origin's internal `µBlock.permanentFirewall` storage or its parsed rule bitmaps. However, any extension with the `declarativeNetRequest` permission can call `chrome.declarativeNetRequest.updateDynamicRules()` to add rules to the browser's shared DNR pool, which uBlock Origin will respect alongside its own rules. These externally added rules operate at the browser level and affect network requests globally.

### What is the difference between session and permanent dynamic rules in uBlock Origin?

**Permanent dynamic rules** are stored in `chrome.storage.local` via uBlock's `µBlock.permanentFirewall` and persist across browser restarts, converted to DNR format during startup in [`platform/mv3/extension/js/ruleset-manager.js`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/ruleset-manager.js). **Session rules** are temporary filters cleared when the browser closes, managed through `chrome.declarativeNetRequest.updateSessionRules()` and used when uBlock's "Temporary" toggle is active in the dynamic filtering pane.

### How does uBlock Origin convert textual filter rules to DNR format?

The conversion occurs in two stages within [`src/js/dynamic-net-filtering.js`](https://github.com/gorhill/uBlock/blob/main/src/js/dynamic-net-filtering.js). First, `DynamicHostRuleFiltering.fromString()` (lines 994-998) parses text like `example.com * 3p block` into an internal bitmap structure. Then, `DynamicHostRuleFiltering.toArray()` (lines 998-1014) serializes this structure into objects with `id`, `priority`, `action`, and `condition` properties that match the Chrome DNR schema required by `chrome.declarativeNetRequest.updateDynamicRules()`.

### What permissions are required to programmatically manage dynamic filter rules?

You must declare the `"declarativeNetRequest"` permission in your extension's [`manifest.json`](https://github.com/gorhill/uBlock/blob/main/manifest.json), as seen in uBlock Origin's [`platform/mv3/extension/js/manifest.json`](https://github.com/gorhill/uBlock/blob/main/platform/mv3/extension/js/manifest.json). For modifying session rules specifically, no additional permissions are required beyond the base DNR permission. However, to replicate uBlock's persistent storage behavior, you would also need access to `chrome.storage.local` or an equivalent persistence mechanism.