DEV Community

Adarsh
Adarsh

Posted on

How I Engineered a Custom Chromium Adblocker from Scratch (Manifest V3)

As a Linux-first developer, my entire workflow is built around granular control. I rely on Neovim and the terminal to keep my environment lean and completely free of bloatware. Recently, I decided to apply that exact same philosophy to my web browser.

Instead of just downloading a black-box extension to block ads, I wanted to understand the mechanics underneath. My engineering philosophy is simple: Build → Break → Optimize.

So, I built and shipped v1.0 of my own Chromium-based adblocker. Here is exactly how it works under the hood using Manifest V3.

The Engine: manifest.json

Every Chromium extension starts with the manifest. With the controversial shift to Manifest V3 (MV3), Google changed how adblockers operate, moving away from the old webRequest API to the newer declarativeNetRequest API.

This means the browser handles the blocking natively based on rules we provide, making it faster and more secure. Here is the core of the engine:

{
  "manifest_version": 3,
  "name": "ZeroBloat Adblocker",
  "version": "1.0",
  "description": "A lightweight, custom adblocker built for speed.",
  "permissions": [
    "declarativeNetRequest",
    "declarativeNetRequestFeedback"
  ],
  "host_permissions": [
    "<all_urls>"
  ],
  "declarative_net_request": {
    "rule_resources": [
      {
        "id": "ruleset_1",
        "enabled": true,
        "path": "rules.json"
      }
    ]
  }
}
Enter fullscreen mode Exit fullscreen mode

rules.json:

[
  {
    "id": 1,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": "||doubleclick.net^",
      "resourceTypes": ["script", "image", "xmlhttprequest"]
    }
  },
  {
    "id": 2,
    "priority": 1,
    "action": { "type": "block" },
    "condition": {
      "urlFilter": "||googleadservices.com^",
      "resourceTypes": ["script", "image"]
    }
  }
]
Enter fullscreen mode Exit fullscreen mode

Breaking Down the Mechanics:

id: Every rule needs a unique integer.

action: { "type": "block" }: This tells the browser to instantly drop the connection before it even resolves.

urlFilter: This uses standard Adblock Plus syntax. The ||means it matches the domain and all subdomains, catching the trackers before they load.

The "Optimize" Phase

Building v1.0 was about getting the core mechanics working. It successfully strips out the heavy tracking scripts and media elements from standard pages, resulting in a much cleaner, faster browsing experience.

The next phase is optimizing the rule sets and exploring how to handle dynamic element hiding without introducing the very bloatware I set out to avoid.

If you are a student or developer who usually relies on out-of-the-box tools, I highly recommend building your own. You learn vastly more about browser architecture by breaking and building the tools you use every single day.

Top comments (0)