How ad blockers work, and how to choose one

Ad blockers get described as "things that remove ads", but in practice two separate layers are at work: one stops the request itself, the other hides whatever is left on screen. Once you separate the two, filter syntax makes sense, and so do the strengths and limits of extensions, DNS blocking and device apps. This article walks through the syntax and then the choice.

Two layers, not one

The first is the network layer. It inspects the addresses a page tries to load and stops those used for ad delivery or tracking. If the request never happens, no image and no script is fetched: bandwidth drops and tracking scripts never run. This is where most of the effect comes from.

The second is the display layer. Anything the network layer could not stop — or the empty frame it left behind — is hidden with CSS. When ads are served from the same domain as the article, blocking the request would take the article with it, so this layer handles those cases.

Filter syntax maps directly onto the two.

// network layer: block requests to ads.example.com and its subdomains, but only from another domain
||ads.example.com^$third-party

// display layer: on news.example.jp, hide elements with class="ad-banner"
news.example.jp##.ad-banner

What the symbols mean

This "Adblock style" syntax is largely shared by uBlock Origin, AdGuard and Adblock Plus. There are not many symbols to learn.

SymbolMeaningExample
||Start of a domain, subdomains included||ads.example.com^
^A separator character (/, ?, : …) or the end||example.com^
*Any text/banner-*.gif
@@Exception: never block what this matches@@||example.jp/assets/*
$Everything after it is a condition$third-party,script
##Hide elements (a CSS selector follows)example.com##.ad
#@#Cancel a hiding ruleexample.com#@#.ad
!Comment line! my own note

The trailing ^ in particular changes the outcome. Written as ||ads.example.com, the rule matches any domain starting with that text, which sweeps in look-alikes such as ads.example.com.evil.net. To end the domain there, write ||ads.example.com^.

Options exist to narrow down when a rule fires. $third-party means "only when loaded from another domain", $script means "only for scripts", and $domain=example.com means "only while that page is open". A broad pattern with no conditions will take necessary requests with it.

Where it runs decides what it can do

ApproachReachCan doLimits
Browser extensionThat browser onlyBoth request blocking and element hiding; sees inside the page, so it can be preciseNo effect on other apps; bound by what the extension platform allows
DNS blockingEvery app on a device or routerConfigured once; reaches ads inside apps tooWhole domains only; cannot hide elements
Device app (VPN-shaped)The whole deviceWorks across appsIt sits in the middle of your traffic, so vendor trust is the deciding factor

The limit of DNS blocking is the part most often misunderstood. It can only act on a domain as a whole, so an ad served from the same domain as the article cannot be stopped — blocking it would remove the article too. When an ad disappears with an extension but survives DNS blocking, this is usually why.

On the extension side, the platform is shifting from "the extension decides on each request" to "the extension hands the browser a declared table of rules and the browser applies it". The declarative form is faster and keeps request contents out of the extension's hands, but it caps how many rules and which conditions you can express. Check the extension's own documentation to see which model your browser and extension use today.

A wrong filter breaks pages quietly

A mistaken filter does not raise an error. It simply fails to match, or matches too much — and both surface in ways that are hard to notice. Here are three common ones, as reported by our filter syntax checker.

About this output: the following was produced on 17 August 2026 by loading the checker's "common mistakes" example as-is. You can reproduce it the same way.
// input
*
http://ads.example.com/
||ads.example.com
##div

// output (abridged)
* … Warning: This matches every request. It will almost certainly break pages.
http://ads.example.com/ … Warning: Writing http:// misses the https:// version.
||ads.example.com … Warning: Without a trailing ^ this can also match look-alike domains.
##div … Warning: Hiding a bare tag name with no domain removes that element on every site.

Every one of these is syntactically valid, so the extension accepts them without complaint. The reliable way to check your own filters is to read each line back as a plain sentence.

Free tool
Filter Syntax Checker
Explains in plain English what each line of your filter blocks and flags mistakes. Enter a URL to see which rules match it. Runs entirely in your browser; nothing is uploaded.

Choosing, in practice

From the site owner's side

Widespread ad blocking directly reduces income for sites funded by advertising. At the same time, readers choosing how pages look on their own devices is not, in itself, a problem. The workable middle is to make it easy for a reader to decide "this site gets allow-listed": keep pages light, cut down on tracking, and offer a way to support the site.

One technical note: if measurement scripts are entangled with rendering, the page stops working the moment they are blocked. Keep the article readable when analytics fail and blocked visitors still get what they came for.

Frequently asked questions

What does an ad blocker actually do?

It works in two layers. The network layer stops the request to an ad or tracking address before anything is fetched. The display layer hides, with CSS, whatever survived that or left an empty frame behind. Filter syntax mirrors this split: a line like ||example.com^ belongs to the network layer, while example.com##.ad-banner belongs to the display layer. Together they produce the impression that the ads have disappeared.

Should I use an extension, DNS blocking, or a device app?

They differ in reach and precision. A browser extension sees inside the page, so it can hide individual elements, but it only works in that browser. DNS blocking applies to every app on a device or router, yet it can only act on whole domains, so blocking a domain that also serves the article body leaves you with nothing. Device apps (often VPN-shaped) reach the widest, but because they sit in the middle of your traffic, how much you trust the vendor becomes the main question.

What happens when a filter is written wrong?

Pages break, quietly. Common cases: dropping the trailing ^ in ||example.com so look-alike domains are caught too; writing http:// so the https:// request never matches; and hiding a bare tag such as ##div with no domain, which removes that element on every site. Our filter syntax checker flags all three as you type.

Is it acceptable to use an ad blocker?

Changing how pages look on your own device is generally fine, though it does thin the ad revenue that keeps many sites running. A practical compromise is to allow-list the sites you want to keep reading and support them where a mechanism exists. Technically, note that every added filter list means more matching work on each page load, so piling lists on can make browsing feel slower rather than faster.

← Back to the blog