This guide covers how to write and maintain your own Adblock Plus filters. Custom filters give you more control over what loads on the sites you visit, including ads, images, requests, and scripts.
Please note that the filter examples in this guide are there to teach, not to copy into your filter list.
About Adblock Plus filter lists
The following filter lists are enabled by default:
-
EasyList (+ bundled language filter list, depending on your browser's language setting)
-
ABP Anti-circumvention filter list
-
Acceptable Ads
In addition to the default filter lists, you'll find more to choose from on ABP’s settings page. You can also write your own filters. A filter is a single rule that tells your browser what to block or hide on a webpage; a filter list is a collection of those rules, maintained over time as websites change.
Types of filters
There are several types of filters, or filter rules, including:
|
Type |
Description |
|---|---|
|
Blocking filters |
Applied on the network level to decide whether a request should be blocked. |
|
Element hiding filters |
Hide particular elements on a page, including element hiding with extended selectors. |
|
Exception filters |
Allowlist or override filters, used to cancel out other filters so a request loads or an element stays visible even when a blocking filter matches it. |
|
Snippet filters |
Run a small piece of JavaScript in the page, for ads that blocking and hiding filters can't tackle. |
|
Snippet exception filters |
Switch off a snippet filter. |
You can create your custom filter(s) via the Adblock Plus settings page.
Blocking filters
A blocking filter stops a network request before your browser loads it. The simplest version is the address of the thing you want gone, for example:
http://example.com/ads/banner123.gif
This filter works as long as the address stays the same, but often it doesn't. The 123 in the filename could be randomized and dynamic, so on your next visit the banner arrives as banner456.gif and your filter misses it.
Wildcards can fix that. A * stands in for any run of characters:
http://example.com/ads/banner*.gif
Filters can also match any part of the request rather than the whole path, so this filter would also work. It blocks everything served from the site's ads directory:
http://example.com/ads/
Note
Shorter patterns catch more, which cuts both ways. http://example.com/ matches every address on the site, so it blocks the banners along with the articles, images, and stylesheets you wanted to see.
Matching the start or end of a request
A filter that matches anywhere in a request address will sometimes match somewhere you didn't intend. js catches http://example.com/annoyingAd.js as you'd expect, and it also catches http://example.com/js/index.html, because "js" is also in the path.
A pipe character (|) pins the filter to one end of the address.
| at the end of a filter means the address has to stop there. js| blocks http://example.com/annoyingAd.js and leaves http://example.com/js/index.html untouched.
| at the start of a filter means the address has to begin there. |http://baddomain.example/ blocks http://baddomain.example/banner.gif, but not http://gooddomain.example/analyze?http://baddomain.example, where the bad address only shows up as a query parameter.
|| at the start of a filter anchors to the beginning of the domain name, which saves you writing out every variation of a URL. A single filter, ||example.com/banner.gif, is considered best practice as it covers all three of these:
http://example.com/banner.gif
https://example.com/banner.gif
http://www.example.com/banner.gif
It still won't touch http://badexample.com/banner.gif or http://gooddomain.example/analyze?http://example.com/banner.gif.
Matching separator characters
Sometimes a filter needs to stop at whatever punctuation comes next, and you can't always predict what that punctuation will be. Say you want to block http://example.com/ and http://example.com:8000/ while leaving http://example.com.ar/ alone. A ^ stands in for one separator character:
||example.com^
A separator is any character that isn't a letter, a digit, _, -, . or %. The end of an address counts as a separator too, which is why a trailing ^ still matches when nothing follows.
Take this URL:
http://example.com:8000/foo.bar?a=12&b=%D1%82%D0%B5%D1%81%D1%82
Its separators are the : and // after the scheme, the : before the port, the / before the path, the ? opening the query string, both = signs, the &, and the end of the address.
The dots in example.com and foo.bar are not separators, which is exactly why filter ||example.com^ does not match on http://example.com.ar/.
Any of these three filters blocks that request:
||example.com^
^foo.bar^
^%D1%82%D0%B5%D1%81%D1%82^
Anchors and separators combine, and the result is the pattern you'll see most often in filter lists. ||example.com^ matches http://example.com/ and its subdomains on any scheme, and it stops at the first separator, so it never spills over to block http://example.com.ar.
Exception rules
When a filter blocks something you wanted to keep, you have a choice: narrow the blocking filter, or leave it as it is and write an exception that allowlists that network request.
@@||example.com/ads/banner.gif
An exception rule overrides any filter matching the same request. Writing one means putting @@ in front of a blocking filter pattern; everything else behaves the same way, wildcards and regular expressions included.
Example
Your custom filter adv is doing its job on network requests fetching ads, but it's also blocking http://example.com/advice.html, which contains legitimate content. Add the exception @@advice as a custom filter and that page displays as expected again, while adv continues to block everything else it matches.
Exceptions can allowlist a whole page rather than a single request. Add the $document option and Adblock Plus stops blocking on the site entirely:
@@||example.com^$document
With the above exception rule in place, all pages of http://example.com remain unblocked.
Blocking filter options and patterns
Filter options
Filter options allow you to specify when a blocking filter applies and which resource it should block. You can restrict a filter to one type of request, to particular domains, to third-party requests, and so on.
Filter options are written after the filter, indicated by $ and separated from each other by commas:
/ads/*$script,match-case
/ads/* is the filter. $script restricts the filter to script requests, and match-case makes the match case-sensitive, so an address containing /Ads/ gets through unblocked.
Options work on blocking filters and on the exception filters that override them. Element hiding filters take a domain prefix instead, covered further down.
Adblock Plus supports these options, marked by $:
|
Option |
Applies to |
Notes |
|---|---|---|
|
|
Scripts loaded via the HTML |
|
|
|
Images, usually loaded via the HTML |
|
|
|
CSS stylesheet files |
|
|
|
Content handled by browser plug-ins |
|
|
|
Requests made with |
|
|
|
Embedded pages, usually inline frames |
|
|
|
Requests from the |
|
|
|
Connections opened through a |
|
|
|
Connections opened through |
|
|
|
External font files |
|
|
|
Audio and video files |
|
|
|
Pages opened in a new tab or window |
Pop-ups load normally unless a filter names this option |
|
|
Request types not covered above |
|
|
|
Web bundle requests |
ABP 4.36 and later. |
Any type option takes a tilde ~ to invert it. $~image applies the filter to every request type except images.
||example.com/tracker^$~image
Exception-only options
Only work on exception filters, starting with @@. On a blocking filter they have no effect.
|
Option |
Effect |
|---|---|
|
|
Turns off all blocking for the page. Use it to allowlist an entire site or iframe |
|
|
Turns off element hiding filters for the page and leaves blocking filters running |
|
|
Turns off generic element hiding filters only, so domain-specific ones keep working |
|
|
Turns off generic blocking filters only, so domain-specific ones keep working |
@@||example.com^$document
@@||example.com^$generichide
Behavior options
|
Option |
Effect |
Notes |
|---|---|---|
|
|
Matches only requests to a domain other than the page's |
|
|
|
Applies the filter only to requests with matching letter case |
|
|
|
Restricts the filter to the listed domains, separated by pipe characters |
Exclude domains using |
|
|
Restricts the filter to pages carrying that sitekey |
List several keys separated by |
|
|
Injects a Content Security Policy header into the response for a page or frame the filter matches |
ABP 3.1 and later. |
|
|
Redirects the request to a built-in placeholder resource instead of blocking it outright |
ABP 3.5 and later. Written as |
|
|
Matches on the value of a response header |
ABP 3.11 and later, Firefox only |
|
|
Injects a header into the request or response |
ABP 4.32 and later. Written as |
Redirecting requests to a blank resource
Sometimes, blocking a script outright breaks the page functionality, because the site's own code expects that file to load and falls over when nothing arrives. $rewrite= covers that case. Rather than blocking the request, Adblock Plus answers it with an empty stand-in of the right type, so the page gets a valid response and the script does nothing.
Name the resource with an abp-resource: prefix:
||example.com/tracker.js^$rewrite=abp-resource:blank-js,domain=example.com
Available in ABP 3.5 or higher:
|
Resource |
Description |
|---|---|
|
|
Empty text |
|
|
An empty stylesheet |
|
|
An empty JavaScript file |
|
|
An empty HTML document |
|
|
A silent MP3, one tenth of a second long |
|
|
A blank MP4 video |
|
|
A 1x1 pixel transparent GIF |
|
|
A 2x2 pixel transparent PNG |
|
|
A 3x2 pixel transparent PNG |
|
|
A 32x32 pixel transparent PNG |
Rules for $rewrite= filters
-
The pattern has to be
*or start with||. -
The filter has to be restricted to domains with the
$domainoption. -
$third-partycan't be combined with$rewrite=.$~third-partyis fine. -
A resource name Adblock Plus doesn't recognize makes the whole filter inert, and the request goes through.
Header matching
$header adds a condition to a blocking filter: the response has to carry a particular HTTP header before the filter blocks anything. The URL pattern still has to match as usual, so the header check narrows an existing filter rather than replacing what it already does. Available in Adblock Plus 3.11 and later.
Note
Header matching filters carry a security risk, so Adblock Plus accepts them from two places only: a user's own custom filters, and the ABP Anti-Circumvention Filter List, which is vetted and reviewed. They also work in Firefox only.
The option takes the form header=name=content. The header name is matched case-insensitively, and the content is matched literally, as a substring of the header's value:
$header=x-http-header=some text
The above blocks the request when the response carries an x-http-header header whose value contains "some text".
To match on a header's presence and ignore its value, drop the content along with the second equals sign:
$header=x-http-header
Commas in the content string
A comma separates one filter option from the next, so a literal comma inside the content has to be escaped as \x2c:
||example.com^$header=x-http-header=some text\x2c and more
That matches a header value containing "some text, and more".
To search for the characters \x2c literally, add another backslash:
||example.com^$header=x-http-header=some text\\x2c and more
That matches a header value containing "some text\x2c and more".
Regular expressions
Regular expressions aren't supported in the content value. For example, $header=/content/ won't behave as intended.
Adding headers to a request or response
$addheader is an option type that doesn't block a resource. Rather than stopping the request, Adblock Plus lets it through and injects a header of your choosing, either into the request on its way out or into the response coming back. Available in ABP 4.32.0 and later.
Note
For security reasons, $addheader filters are accepted from two places only: a user's own custom filters, and ABP's vetted and reviewed lists such as the ABP Anti-Circumvention Filter List and the Acceptable Ads List.
The option value has three parts separated by colons, written as type:http-header:content.
|
Part |
Meaning |
|---|---|
|
|
Either |
|
|
The name of the header to inject. It has to be a valid HTTP header name starting with |
|
|
The value to give the header. Printable ASCII characters only |
||example.com^$addheader=response:x-some-header:some text
The filter above injects x-some-header: some text into responses from http://example.com. Only the first two colons act as separators, so a value containing colons of its own works as written.
What $addheader can and can't do
-
It belongs on blocking filters only. An exception filter can't carry it.
-
Combine it with the
documenttype to reach top-level page requests alone. -
An exception filter matching the same request won't stop the injection. To switch it off, allowlist the page with an exception filter carrying
$document. -
A handful of headers are refused even with the
x-prefix:x-content-type-options,x-download-options,x-frame-options,x-permitted-cross-domain-policies,x-powered-by, andx-xss-protection.
Regular expressions
When ABP's wildcards * can't express the pattern you need, you can write the whole filter as a regular expression by wrapping it in forward slashes. /banner\d+/ matches banner123 and banner321, and it leaves banners alone, because \d+ needs at least one digit.
Regex is written inside the slashes, so ^, * and | carry their regular expression meanings rather than the ABP ones described above. Mozilla's guide to regular expressions covers the syntax.
Note
For performance reasons, we recommend avoiding regular expressions if possible. Only reach for regex when a plain pattern won't do the job.
Comments
Start a line with an exclamation mark and Adblock Plus skips it entirely. Write human-readable comments after the !.
! Blocks the sticky newsletter bar
example.com##.newsletter-bar
Comments are typically placed above a filter and used to describe its function, as well as filter list information such as authorship and licensing that you may find at the top of a filter list.
When Adblock Plus downloads a filter list, it reads certain comments as metadata about the list. They apply to downloaded lists only, so they do nothing among your own custom filters. The parameters they can set are:
|
Comment |
Purpose |
|---|---|
|
|
Filter list homepage or source repository |
|
|
Name of filter list |
|
|
Sets the update interval for the filter list |
|
|
Indicates that the filter list has moved to a new download or redirect address |
|
|
Defines a numerical version of the filter list |
Element hiding filters
Some ads cannot be tackled with blocking filters, as they're written into the page's HTML alongside the rest of the article, so there's no separate network request to block.
Element hiding filters (also known as content filters) cover this gap. Rather than acting on requests, they act on the content of the page itself. They point at an element on the page and hide it.
Rule syntax
Every hiding filter follows this structure:
<domains><separator><body>
|
Part |
Description |
|---|---|
|
|
The sites the filter applies to, separated by commas. Leave it empty and the filter applies everywhere. Prefix a domain with |
|
|
Marks where the domain list ends, and sets which type of content filter this is |
|
|
Which element the filter acts on |
example.com##.ad-banner
example.com is the domain, ## makes this an element hiding filter, and .ad-banner is the CSS class for the element to hide.
Each separator and what it does:
|
Separator |
Type |
Body content |
|---|---|---|
|
|
Element hiding |
CSS selector |
|
|
Extended element hiding |
Extended CSS selector |
|
|
Element hiding exception |
CSS selector |
Basic element hiding filters
Right-click the ad you want gone and click Inspect. Your browser opens its developer tools with the element selected, and you might see markup like this:
<div class="textad">
Cheapest tofu, only here and now!
</div>
<div id="sponsorad">
Really cheap tofu, click here!
</div>
<textad>
Only here you get the best tofu!
</textad>
Each one can be hidden with a CSS selector in the body of the filter:
|
Filter |
Hides |
Matched by |
|---|---|---|
|
|
The first ad |
Its |
|
|
The second ad |
Its |
|
|
The third ad |
Its element name |
A filter with no domain in front of it is called generic, and it applies on every site you visit. All three filters above are generic, which can sometimes cause false positives. The next section, on domain restrictions, covers how to narrow a filter to the sites that need it.
Restricting a filter to certain domains
Generic filters may cause false positives: the same selector that hides an ad on one site could hide legitimate content on another.
example.com##.sponsor
This filter applies on example.com and its subdomains, such as something.example.com, and leaves example.org alone. Where a page embeds iframes, the filter reaches inside any iframe whose URL matches as well.
Separate several domains with commas:
domain1.com,domain2.com,domain3.org##.sponsor
Excluding a domain
A ~ in front of a domain tells the filter to skip it.
|
Filter |
Where it runs |
|---|---|
|
|
Applies everywhere except |
|
|
Applies on |
Matching any top-level domain
When a site runs on several top-level domains, * stands in for the TLD:
example.*##.ad
That covers example.com, example.co.uk, example.de and the rest, which saves listing each one by hand.
Note
Domain restrictions work on the domain names only, nothing else in the address.
-
Paths are ignored.
example.com/news##.adwon't limit the filter to the news page. There's no way to restrict a content filter to part of a site. -
A bare name isn't a shortcut.
example##.addoesn't coverexample.comandexample.org. List each one:example.com,example.org##.ad -
Subdomains come for free.
example.com##.adalready applies onnews.example.com, so there's nothing extra to write. -
*is the only wildcard, which replaces the top-level domain:example.*##.ad
Attribute selectors
Some ads don’t carry a class or ID to target. Any other attribute on the element works just as well:
|
Filter |
Hides |
|---|---|
|
|
Tables whose |
|
|
Divs whose |
|
|
Divs whose |
|
|
Divs whose |
Stack conditions and the element has to match all of them:
##div[title^="adv"][title$="ert"]
##table[width="80%"][bgcolor="white"]
The first filter hides divs whose title starts with "adv" and ends with "ert". The second hides tables that are 80% wide with a white background.
Advanced selectors
Any CSS selector your browser supports can go in the body of an element hiding filter, combinators and pseudo-classes included.
##.adheader + *
This filter hides whatever element comes immediately after the element with a class adheader.
Browsers match these more slowly than simple class or ID selectors, so use a simple selector where one will do. MDN's CSS selectors reference lists what is available.
Note
This section assumes you're comfortable with CSS. Adblock Plus rejects a filter whose selector isn't valid CSS, so a typo means the filter won't save at all.
Inline CSS styles
Instead of hiding an element, a content filter can restyle it. Follow the selector with a space and a CSS declaration block, and those styling properties will be applied to the matching elements:
example.com##.banner { margin-bottom: 0 }
This is commonly used for tidying up. For example, hiding an ad may leave an empty box or a stray margin behind, so a small CSS fix can help.
Available from ABP 4.38.1 and AdBlock 6.41.1.
What you can write
Property names have to be real CSS property names, and they can't start with a double dash, so CSS custom properties are not accepted.
Values are restricted to the list below. Anything outside it is rejected for security reasons.
Global keywords
inherit, initial, none, revert, revert-layer, unset
Colors
Hex colors only, such as #009900. Also currentcolor and transparent.
Numbers
Any number, with or without a unit: 0, 10px, 50%, 1.5em.
Accepted units: cm, mm, q, in, pc, pt, px, em, ex, ch, rem, lh, rlh, vw, vh, vmin, vmax, vb, vi, svw, svh, lvw, lvh, dvw, dvh, %, fr
Keywords
absolute, all, auto, block, both, clip, collapse, contain, dashed, default, dotted, double, element, fit-content, fixed, flex, flow, flow-root, grid, groove, hidden, inline, inline-block, inline-flex, inline-grid, inline-table, inset, left, list-item, max-content, min-content, outset, pointer, relative, ridge, right, ruby, scroll, solid, static, sticky, table, table-row, text, thin, true, visible
Remove action
Hiding an element makes it invisible by setting CSS display: none;, which leaves it on the page. Scripts can still find it, and selectors that count siblings still see it. remove: true; takes the element out of the DOM instead.
It goes in the filter body as a CSS property would:
example.com##.banner { remove: true; }
Note
A filter using the remove action will take precedence over ones using other CSS properties.
Extended CSS selectors
Some ads can't be targeted with ordinary CSS, so extended selectors can be helpful in those cases:
|
Selector |
What it matches |
Alias |
|---|---|---|
|
|
Elements by what they contain |
|
|
|
Elements by their text |
|
|
|
Elements by their computed CSS property values |
|
|
|
Elements that don't match the inner selector |
|
|
|
Elements matched by an XPath expression |
|
Filters using :has() and :not() use the basic syntax ##.
A filter using :-abp-has(), :-abp-contains(), :-abp-properties(), :has-text() or :xpath() extended CSS selector requires the #?# separator:
example.com#?#section:-abp-has(> div > a.advertiser)
They cost more to match than ordinary CSS, so use them sparingly and scope such filters to as few domains and elements as you can.
Prefer :has() and :has-text()
Write filters using :has() and :has-text() rather than the -abp- equivalents. Both versions still work, but since :has() is standard CSS now, it’s supported in every current browser and performs better. Write it in the basic filter syntax ## and the browser matches it directly:
example.com##section:has(> div > a.advertiser)
Using :has-text() allows for better compatibility and is used in open-source filter lists such as EasyList.
example.com#?#aside.card:has-text(Ad)
Note
A filter like example.com##:has(.sponsored) could hide the entire page, because html and body contain .sponsored too. Target an element in front of the pseudo-class and scope the inner selector: example.com##div.container:has(> .sponsored)
Deciding which to use
|
What you're matching on |
Use |
Example |
|---|---|---|
|
DOM structure, elements alone |
|
|
|
Text |
|
|
|
Combination of elements & text |
|
|
|
Elements that do not match |
|
|
|
Computed style |
|
|
|
XPath |
|
|
Note
When using the background-color CSS property, use rgb() notation. For example,
example.com#?#.product-card:-abp-properties(background-color: rgb(61, 156, 79)).
Element hiding exception rules
An exception rule switches off an existing element hiding filter on defined domains. Every exception filter uses #@#:
##.textad
example.com#@#.textad
The above filters have the same result as ~example.com##.textad on its own. Depending on the use case and the filter source, you may define which domain to exclude by using ~ or writing a separate exception rule using #@#.
The selector has to match exactly
An exception allowlists one specific filter, matched on its selector exactly.
|
Filter |
Exception |
Result |
|---|---|---|
|
|
|
Nothing allowlisted |
|
|
|
Nothing allowlisted |
|
|
|
Allowlisted, |
Filters with styles or a remove action
When the filter carries a declaration block, the exception needs the selector alone:
##aside.info {remove: true}
example.com#@#aside.info
Snippet filters
Some ads survive both blocking and hiding, usually because scripts on the page build or restore them as it runs. Snippet filters deal with those by running a small piece of JavaScript code on the page itself.
The body names the snippet and passes any arguments it takes, after the #$# separator:
example.com#$#hide-if-contains "Sponsored" div
Snippet filters must be specific to domains. The snippet filters tutorial covers the available snippets and how to write them.
Note
Because snippets run code in the page, Adblock Plus accepts them only from two sources: a user's custom filters, and the vetted and reviewed ABP Anti-Circumvention Filter List.
Snippet exceptions
From ABP 4.45.0 and AdBlock 6.47.0, #@$# switches off a snippet on the domains defined:
example.com#@$#hide-if-contains "Sponsored" div
Snippet exception requires the same command and arguments as the snippet it disables.
Snippet exceptions don’t require a privileged list, and they don't need a domain defined, so a snippet exception with no domain applies globally.
Appendix: Implementing a sitekey on the server
To use sitekey-restricted filters, your pages have to present a public key and a signature ABP can verify. Both values are base64-encoded, and both go out in two places.
An HTTP response header:
X-Adblock-Key: abcdpublickeydcba_abcdsignaturedcba
And the document's root element:
<html data-adblockkey="abcdpublickeydcba_abcdsignaturedcba">
Both are needed, since different browsers read different ones.
The key
Create an RSA private key and a DER representation of the matching public key. 2048 bits is the minimum, 4096 is better.
The signature
For each request, build a string from three values joined with NUL characters (\0): the request path including its query string, the host, and the requesting browser's user agent.
/index.html?q=foo\0www.example.com\0Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:30.0) Gecko/20100101 Firefox/30.0
Sign that string with your private key, using RSA with ⟨digest⟩, and base64-encode the result.
Because the signed data includes the request URI and the user agent, the signature is different for every request, so your server has to compute it per response rather than storing one.