Edit (25th November 2022):
My blog got listed on EasyList
Therefore, I had to remove all AdBlock baiting JavaScripts from this blog. This means that the AdBlock Detection on this blog no longer works.
I am sorry I can not longer offer AdBlock detection here. I still think that it is valid for publishers to know whether clients are blocking ads. Publishing content is hard work and should be rewarded in some form. Intrusive ads are annoying, I get why clients are blocking them. But there is some middle ground I guess.
- For the code, visit the GitHub page of this article
- Alternatively, install the Adblock detection script from npm with the command
npm i adblock-detect-javascript-only
In case this will stop working in the next days / weeks, I will make the selection of filter dynamic and random. Put differently: If you whitelist a filter such as pp34.js?sv=
(uBlock Origin) or &ad_height=
(EasyList - uBlock Origin and Adblock Plus), I will make a random selection of a filter / list entry in the following block-lists:
- Adblock EasyList: https://github.com/easylist/easylist/blob/master/easylist/easylist_general_block.txt
- uBlock Origin uAssets: https://github.com/uBlockOrigin/uAssets/blob/master/filters/filters-2022.txt
Adblock Plus Detected:
uBlock Origin Detected:
Update Adblock / uBlock Origin Detection on August 27th 2022
This is the newest detection code:
/**
* Author: Nikolai Tschacher
* Updated: 6th November 2022
* Website: https://incolumitas.com/
*
* Detects uBlock Origin, Adblock Plus and AdBlocker Ultimate with JavaScript only.
*
* Usage: detectAdblock().then((res) => { console.log(res) });
*
*/
function detectAdblock() {
const adblockTests = {
// https://github.com/uBlockOrigin/uAssets/blob/master/filters/filters-2022.txt
uBlockOrigin: {
url: 'https://incolumitas.com/data/yzfdmoan.js',
id: '837jlaBksSjd9jh',
},
// https://github.com/easylist/easylist/blob/master/easylist/easylist_general_block.txt
adblockPlus: {
url: 'https://incolumitas.com/data/utep_ad.js',
id: 'hfuBadsf3hFAk',
},
};
function canLoadRemoteScript(obj) {
return new Promise(function (resolve, reject) {
var script = document.createElement('script');
script.onload = function () {
if (document.getElementById(obj.id)) {
resolve(false);
} else {
resolve(true);
}
}
script.onerror = function () {
resolve(true);
}
script.src = obj.url;
document.body.appendChild(script);
});
}
return new Promise(function (resolve, reject) {
let promises = [
canLoadRemoteScript(adblockTests.uBlockOrigin),
canLoadRemoteScript(adblockTests.adblockPlus),
];
Promise.all(promises).then((results) => {
resolve({
uBlockOrigin: results[0],
adblockPlus: results[1],
usingAdblock: (results[0] === true) || (results[1] === true),
});
}).catch((err) => {
reject(err);
});
});
}
Usage:
detectAdblock().then((res) => { console.log(res) });
Introduction
uBlock Origin and Adblock Plus are famous anti advertisement browser extensions. Adblock software filter advertisement content from websites. Some folks consider the blocking of ads to be unethical, since publishers lose revenue. Other people regard the blocking of advertisements as their good right. I personally tend to be on the side of the latter group, since ads are way to obnoxious in general (Especially on YouTube).
On the technical side, uBlock Origin and Adblock Plus are designed quite straightforward. They make use of large text based filter lists and compare the items of those lists with the contents of HTML nodes or URLs. If there is a match, the HTML element is removed or the URL is not loaded. An example for such a list would be the well known Easy List. The filter lists for uBlock Origin can be found on their GitHub repo.
However, sometimes it is necessary to be able to detect that an Adblocker is active. Ideally, the detection should work by using vanilla JavaScript only. In this blog post, I will show several different techniques to detect the presence of Adblock software.
All code snippets were tested with Firefox/84.0 and Chrome/86.0.4240.75. On the Firefox browser, Adblock Plus is running. uBlock Origin is activated on Chrome.
Attempt 1: Detect Adblock with a baiting div node
With this technique, the idea is to create an <div>
element dynamically and set the class attribute to adsbox
. If an Adblocker is active, it should automatically remove this div element, because the class name is flagged as suspicious.
The technique is being used in the fingerprint.js library.
Try it out by pasting the code below in your browser JavaScript console.
(function detectWithAdsDiv() {
var detected = false;
const ads = document.createElement('div');
ads.innerHTML = ' ';
ads.className = 'adsbox';
try {
document.body.appendChild(ads);
var node = document.querySelector('.adsbox');
detected = !node || node.offsetHeight === 0;
} finally {
ads.parentNode.removeChild(ads);
}
console.log('Using Adblocker: ' + detected);
})();
This technique did not work on both tested browsers. Therefore, the above code is obsolete.
The same technique was tried with other CSS class names, but nothing worked reliably.
Attempt 2: Detect Adblock by downloading suspicious ad scripts
After some searching in the Internet, I found the following blog post.
The idea is the following: The author tried to make a fetch()
request to a advertisement script, if the request is getting blocked, there must be an active Adblocker in the background.
For testing purposes, a goodURL
is picked that should not get blocked, since it is a widely used JavaScript library for interactive content. On the other side, the badURL
is a URL that points to a Google Advertising script. The badURL
is listed in the Adblock filter list.
var goodURL = 'https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js';
var badURL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
The code from the mentioned blog post was a bit modified. But essentially, it boils down to the following:
var badURL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
(function detectWithFetch() {
var t0 = performance.now();
var myRequest = new Request(badURL, {
method: 'HEAD',
mode: 'no-cors',
});
fetch(myRequest)
.then(response => response.text())
.then(function(data) {
console.log('Not Using Adblock');
})
.catch(function(e){
console.log(e)
console.log('Using Adblock');
})
.finally(function(e) {
var t1 = performance.now();
console.log('Took ' + (t1-t0).toFixed(2) + 'ms');
})
})();
Unfortunately, this also does not work reliably. There are a couple of reasons why the above technique is not ideal:
- The
fetch()
API is still not supported in all browsers because it is relatively new fetch()
tends to have issues with CORS requests
A first solution: Dynamically creating a <script>
tag
Dynamically creating a <script>
tag is a better idea, since <script>
tags are supported in every browser
and the issues with CORS and the same origin policy do not apply.
The following solution works reliably in both Firefox and Chrome. Both Adblock plugins could be detected with the below snippet:
var badURL = 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js';
(function detectWithScriptTag() {
var t0 = performance.now();
var script = document.createElement('script');
script.onload = function() {
var elapsed = (performance.now() - t0).toFixed(2) + 'ms';
console.log("Not using Adblocker, script loaded in " + elapsed);
// delete script node
script.parentNode.removeChild(script);
};
script.onerror = function() {
var elapsed = (performance.now() - t0).toFixed(2) + 'ms';
console.error("Using Adblocker, script failed to load after " + elapsed);
}
script.src = badURL;
document.body.appendChild(script);
})();
After pasting the above snippet into the developer console, we see the error net::ERR_BLOCKED_BY_CLIENT which indicates that uBlock Origin intercepted the request and aborted it. It can also be seen that the request took only 7.96ms, which is way too fast for a legit HTTP request.
When using the above technique in production, several points need to be considered:
- Every time the above script is executed without a active Adblock software, a HTTP request is made and a script is downloaded. This costs network bandwidth resources.
- If the
badURL
is no longer an URL that is on the Adblock filter list, the technique ceases to work. Therefore, the validity of thebadURL
needs to be ensured.
The ultimate solution: Making a request to a non-existent baiting resource
Assuming that most browsers do not have adblock software installed, we want a detection method that is fast and doesn't waste resources. However, the solution presented above does waste unnecessary resources: On a browser without adblock, every time the function is executed, the browser loads the advertisement script.
One idea would be to take a non-existent URL that is universally detected by adblock software, but will not properly load in the case when there is no adblock software installed.
This is the ultimate adblock detection solution. You can paste it in your developer console and it should be able to detect any adblock software. It also supports a fallback to XMLHttpRequest
in case the fetch()
API is not available.
// Author: Nikolai Tschacher
// Date: 28.12.2020
// Website: https://incolumitas.com/
(function detectAdblockWithInvalidURL(callback) {
var flaggedURL = 'pagead/js/adsbygoogle.js';
if (window.fetch) {
var request = new Request(flaggedURL, {
method: 'HEAD',
mode: 'no-cors',
});
fetch(request)
.then(function(response) {
if (response.status === 404) {
callback(false);
}
})
.catch(function(error) {
callback(true);
});
} else {
var http = new XMLHttpRequest();
http.open('HEAD', flaggedURL, false);
try {
http.send();
} catch (err) {
callback(true);
}
if (http.status === 404) {
callback(false);
}
}
})(function(usingAdblock) {
console.log("Using Adblocker: " + usingAdblock);
})