# How uBlock Origin Uses Hostname Switches for Per-Site Configuration

> Discover how uBlock Origin uses hostname switches for per-site configuration through bit-fields and hierarchical domain lookup. Customize blocking preferences effectively.

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

---

**uBlock Origin implements per-site configuration by storing blocking preferences as 32-bit bit-fields called hostname switches, which are evaluated through a hierarchical domain lookup that inherits settings from parent domains unless overridden.**

uBlock Origin's granular control over filtering behavior relies on **hostname switches**, a compact configuration system that attaches per-site settings directly to domain names. These switches enable users to override global rules for specific sites while automatically propagating settings to subdomains. According to the gorhill/uBlock source code, this mechanism is implemented in [`src/js/hnswitches.js`](https://github.com/gorhill/uBlock/blob/main/src/js/hnswitches.js) using a `DynamicSwitchRuleFiltering` class that manages bit-field storage and hierarchical lookups.

## The Hostname Switch Data Structure

The core of the system resides in the `DynamicSwitchRuleFiltering` class, which maintains a Map of hostnames to 32-bit integers representing switch states.

### The 32-Bit Bit-Field Implementation

In [`src/js/hnswitches.js`](https://github.com/gorhill/uBlock/blob/main/src/js/hnswitches.js), the constructor initializes `this.switches` as a Map where each hostname maps to a 32-bit integer:

```javascript
class DynamicSwitchRuleFiltering {
    constructor() {
        this.reset();
    }
    reset() {
        // Map: hostname → 32-bit integer holding the bits for all switches
        this.switches = new Map();
        …
    }
    …
}

```

Each switch consumes **two bits** within this integer to represent three possible states: `0` (inherit from parent), `1` (enabled), or `2` (forced off). This compact representation allows a single 32-bit value to store the state of up to 16 different switches per hostname.

### Switch Offset Definitions

The bit positions for each switch are defined in the `switchBitOffsets` object:

```javascript
const switchBitOffsets = {
    'no-strict-blocking': 0,
    'no-popups': 2,
    'no-cosmetic-filtering': 4,
    'no-remote-fonts': 6,
    'no-large-media': 8,
    'no-csp-reports': 10,
    'no-scripting': 12,
};

```

Available switches include **no-scripting**, **no-cosmetic-filtering**, **no-large-media**, and **no-popups**, among others. Each offset indicates the starting bit position for that switch's 2-bit state field.

## Hierarchical Evaluation Algorithm

When uBlock Origin needs to determine if a switch applies to a specific site, it invokes the `evaluateZ()` method, which performs a bottom-up traversal of the domain hierarchy.

### The evaluateZ Method

The `evaluateZ()` method in [`src/js/hnswitches.js`](https://github.com/gorhill/uBlock/blob/main/src/js/hnswitches.js) accepts a `switchName` and `hostname`, then walks the domain tree from most specific to least specific:

```javascript
evaluateZ(switchName, hostname) {
    const bitOffset = switchBitOffsets[switchName];
    …
    for (const shn of decomposeHostname(hostname, decomposedSource)) {
        let bits = this.switches.get(shn);
        if (bits === undefined) { continue; }
        bits = bits >>> bitOffset & 3;
        if (bits === 0) { continue; }          // inherit → keep walking
        this.z = shn;                           // the hostname that decided
        this.r = bits;
        return bits === 1;                      // true if enabled
    }
    this.r = 0;
    return false;
}

```

The method returns `true` if the switch is enabled, `false` if disabled or not found, and stores the matching hostname in `this.z` for reference.

### Domain Decomposition Strategy

The lookup relies on `decomposeHostname()` from [`src/js/uri-utils.js`](https://github.com/gorhill/uBlock/blob/main/src/js/uri-utils.js), which transforms a hostname like `sub.example.com` into an array `["sub.example.com", "example.com", "*"]`. This enables the switch manager to check for rules at the exact subdomain, the parent domain, and finally the global wildcard level, ensuring **inheritance** works correctly throughout the domain tree.

## Integration with Filtering Engines

Hostname switches integrate with uBlock Origin's primary filtering components to enforce per-site policies at both the network and page levels.

### Network Request Filtering

In [`src/js/ublock.js`](https://github.com/gorhill/uBlock/blob/main/src/js/ublock.js), the engine queries `sessionSwitches.evaluateZ()` to determine whether to block network requests based on site-specific rules:

```javascript
if (sessionSwitches.evaluateZ(details.name, details.hostname) === false) {
    // request is allowed
}

```

This check enables features like **no-large-media** blocking or **no-remote-fonts** restrictions to be applied selectively per domain.

### Page-Level Policy Enforcement

The [`src/js/pagestore.js`](https://github.com/gorhill/uBlock/blob/main/src/js/pagestore.js) module uses the same evaluation method to control page-wide behaviors such as cosmetic filtering and script execution:

```javascript
if (sessionSwitches.evaluateZ('no-large-media', fctxt.getTabHostname()) !== true) {
    // allow large media on this site
}

```

These checks occur during page loading, allowing uBlock Origin to disable specific filtering engines for sites where they might break functionality.

## Managing Switch States

Users manipulate hostname switches through the popup UI or dashboard, which trigger specific toggle methods that handle both immediate and persistent storage.

### Toggle Operations and Deep Propagation

The `toggleZ()` method in [`src/js/hnswitches.js`](https://github.com/gorhill/uBlock/blob/main/src/js/hnswitches.js) provides the interface for changing switch states:

```javascript
toggleZ(switchName, hostname, deep, newState) {
    if (deep) {
        return this.toggleBranchZ(switchName, hostname, newState);
    }
    return this.toggleOneZ(switchName, hostname, newState);
}

```

When the **`deep` parameter is `true`**, the method invokes `toggleBranchZ()`, which applies the change to the target hostname and **clears any descendant overrides**. This ensures the new state propagates correctly to all subdomains without conflicting legacy rules.

### Persistence: Session vs. Permanent Storage

uBlock Origin maintains two instances of the switch manager:

- **`permanentSwitches`**: Stored in [`src/js/filtering-engines.js`](https://github.com/gorhill/uBlock/blob/main/src/js/filtering-engines.js) at line 23, persisted across browser sessions
- **`sessionSwitches`**: Stored in the same file at line 34, held in memory only and cleared on restart

At startup, [`src/js/start.js`](https://github.com/gorhill/uBlock/blob/main/src/js/start.js) (line 344) merges permanent rules into the active session:

```javascript
sessionSwitches.assign(permanentSwitches);

```

This architecture allows users to test temporary configurations without affecting their saved preferences.

## Practical Implementation Examples

### Enabling No-Scripting for a Specific Domain

To programmatically enable the no-scripting switch for `example.com`:

```javascript
import sessionSwitches from './filtering-engines.js';

// Enable no-scripting for example.com (1 == true)
sessionSwitches.toggle('no-scripting', 'example.com', 1);

```

This immediately blocks JavaScript execution on the specified domain.

### Querying Switch State Programmatically

To check if a switch is active for the current tab:

```javascript
import { hostnameFromURI } from './uri-utils.js';
import sessionSwitches from './filtering-engines.js';

function isNoScriptingAllowed(tabUrl) {
    const hn = hostnameFromURI(tabUrl);
    return sessionSwitches.evaluateZ('no-scripting', hn);
}

// Usage
if (isNoScriptingAllowed('https://sub.example.com/page')) {
    console.log('Scripts are blocked on this site');
}

```

### Configuring Deep Subtree Rules

To apply a switch change to an entire domain tree, removing any conflicting subdomain rules:

```javascript
// Turn off "no-cosmetic-filtering" for the entire example.com tree
sessionSwitches.toggleZ('no-cosmetic-filtering', 'example.com', true, false);

```

The `true` flag ensures the change propagates to all subdomains by clearing their existing overrides.

## Summary

- **Hostname switches** in uBlock Origin are stored as 32-bit bit-fields in a Map structure, with each switch occupying 2 bits to represent inherit, enabled, or forced-off states.
- The **`evaluateZ()`** method performs hierarchical lookups using **`decomposeHostname()`**, checking from the specific subdomain up to the wildcard level to respect inheritance rules.
- Two storage instances exist: **`permanentSwitches`** for persistent configuration and **`sessionSwitches`** for temporary in-memory rules.
- The **`toggleZ()`** method supports **deep propagation**, which clears descendant overrides when applying changes to parent domains.
- Integration occurs in **[`src/js/ublock.js`](https://github.com/gorhill/uBlock/blob/main/src/js/ublock.js)** for network filtering and **[`src/js/pagestore.js`](https://github.com/gorhill/uBlock/blob/main/src/js/pagestore.js)** for page-level policies like cosmetic filtering and script blocking.

## Frequently Asked Questions

### What is a hostname switch in uBlock Origin?

A hostname switch is a per-site configuration mechanism that stores blocking preferences (like disabling scripts or cosmetic filters) as a bit-field attached to a specific domain name. According to the source code in [`src/js/hnswitches.js`](https://github.com/gorhill/uBlock/blob/main/src/js/hnswitches.js), each switch uses 2 bits within a 32-bit integer to represent whether the feature is inherited, enabled, or forced off for that hostname.

### How does uBlock Origin handle switch inheritance across subdomains?

When evaluating a switch, uBlock Origin calls `decomposeHostname()` from [`src/js/uri-utils.js`](https://github.com/gorhill/uBlock/blob/main/src/js/uri-utils.js) to generate a hierarchy from the specific subdomain to the root, including a wildcard. The `evaluateZ()` method walks this array from most specific to least specific, using the first non-inherited value it encounters. This allows parent domain rules to automatically apply to subdomains unless explicitly overridden.

### What is the difference between sessionSwitches and permanentSwitches?

`permanentSwitches` persists user configurations across browser restarts in [`src/js/filtering-engines.js`](https://github.com/gorhill/uBlock/blob/main/src/js/filtering-engines.js), while `sessionSwitches` exists only in memory and resets when the browser closes. At startup, [`src/js/start.js`](https://github.com/gorhill/uBlock/blob/main/src/js/start.js) merges permanent rules into the session via `sessionSwitches.assign()`, allowing users to test temporary changes without modifying saved settings.

### How do deep toggle operations work in the hostname switch system?

When calling `toggleZ()` with `deep` set to `true`, the system invokes `toggleBranchZ()`, which applies the switch state to the target hostname and recursively clears any conflicting states from descendant domains. This ensures clean inheritance throughout the domain tree, preventing stale subdomain overrides from interfering with the new parent-level policy.