ad-blockguard
v0.1.0-pre
Published
Universal AdBlock & DNS-Blocker detection for web, WP, React, Vue & Nuxt
Maintainers
Readme
🛡️ (AD-)BlockGuard
AdBlock & DNS-Blocker detection for websites/web apps.
Most users dont hide ads anymore, but block ad domains at network or DNS level (DNS, Pi-hole, VPNs). This finds DNS/network blocking and visual blockers. Protect ad revenue and analytics.
How it works
- Network Test: Sends requests to ad-networks. If enough fail, an ad blocker is detected. This catches DNS blockers, Pi-hole, and VPNs, AD-Blockers.
- Banner Scan: Injects hidden elements with ad-like names and watches them with MutationObserver.
Please fully test your implementation using debug: true
I recommend using AD-domains which are used by your own ad/analytics functions, by specifying them in the configuration. (testUrls as string[])
Installation
npm install ad-blockguardCDN
<script src="https://cdn.jsdelivr.net/npm/ad-blockguard/dist/blockguard.min.js" crossorigin="anonymous"></script>With integrity:
<script
src="https://cdn.jsdelivr.net/npm/ad-blockguard/dist/blockguard.min.js"
integrity="sha256-replace.with.hash"
crossorigin="anonymous">
</script>Generate hash:
openssl dgst -sha256 -binary blockguard.min.js | openssl base64 -A
Quick Start
Vanillajs / CDN
<script src="https://cdn.jsdelivr.net/npm/ad-blockguard/dist/blockguard.min.js"></script>
<script>
var BGClass = window.BlockGuard.default || window.BlockGuard;
new BGClass({
debug: true,
testUrls: ["https://adsense232.com/adsense-script.js", "https://cdn87.analytics.com/script.js", "https://matomoserver.host/watch"],
bannerScan: true,
customBaitClasses: ["banner-names", "similar to", "my served-ones", "pub-738360"]
onDetected: function () {
//you could even bind this to window.gtag being not available
document.getElementById('adblock-warning').style.display = 'block';
},
onNotDetected: function () {
alert("Thanks for not using an ADBlocker, you get 0.0̅01 BTC for this. 🙂")
}
});
</script>
<div id="adblock-warning" style="display:none">
Please disable your ad blocker to support this site.
</div>React / Next.js / Vite + React
Install and create a client component:
// components/AdBlockGuard.jsx
'use client';
import { useBlockGuard } from 'ad-blockguard/react';
export default function AdBlockGuard() {
const { detected, checking } = useBlockGuard({
networkThreshold: 2,
gtag: true,
});
if (checking) return null;
if (!detected) return null;
return (
<div className="adblock-overlay">
<p>Please disable ad blocker to continue. 🙏</p>
<button onClick={() => window.location.reload()}>disabled it</button>
</div>
);
}Add it to your main layout to run on all pages:
// app/layout.jsx
import AdBlockGuard from '@/components/AdBlockGuard';
export default function RootLayout({ children }) {
return (
<html>
<body>
<AdBlockGuard />
{children}
</body>
</html>
);
}Vue 3 / Nuxt 3
<!-- components/AdBlockGuard.vue -->
<script setup>
import { useBlockGuard } from 'ad-blockguard/vue';
const { detected, checking } = useBlockGuard({
networkThreshold: 2,
gtag: true,
});
</script>
<template>
<Teleport to="body">
<div v-if="!checking && detected" class="adblock-overlay">
<p>Please disable your ad blocker to support this site.</p>
<button @click="$router.go(0)">I've disabled it</button>
</div>
</Teleport>
</template>Nuxt SSR note: The composable auto-detects server-side rendering and skips all DOM operations safely.
Astro
make component to use later in root layout file:
***
// src/components/AdBlockGuard.astro
***
<div id="bg-warning" style="display:none">
Please disable your ad blocker.
</div>
<script>
import BlockGuard from 'ad-blockguard';
new BlockGuard({
onDetected: () => {
document.getElementById('bg-warning').style.display = 'block';
}
});
</script>Then import the component into your main layout and use it:
***
// src/layouts/Layout.astro
***
---
import AdBlockGuard from '../components/AdBlockGuard.astro';
---
<!doctype html>
<html lang="en">
<head>
// here were some meta tags
<title>Astro</title>
</head>
<body>
<AdBlockGuard />
<slot />
</body>
</html>WordPress
A WordPress plugin is available as .zip file, see /integrations/wordpress. (you'll also find a tutorial on how to use it there)
Configuration
All options are optional, but configuration is recommended. Pass them to new BlockGuard({ ... }) or useBlockGuard({ ... }). (new ... is for pure JS/Astro. the second one is for hooks(react/nuxt/vue/vite/next.js))
Detection
| Option | Type | Default | Description |
| :--- | :---: | :---: | :--- |
| checkOnLoad | boolean | true | Auto-run detection when the page loads. Automatically set to false in React/Vue hooks. |
| networkTest | boolean | true | Detect DNS/network-level blockers (AdGuard DNS, Pi-hole, VPNs) via HEAD requests. |
| bannerScan | boolean | false | Detect visual ad blockers (e.g. uBlock Origin) by injecting hidden bait elements. |
| debug | boolean | false | Print detailed logs to the browser console. Useful during development. |
Network Test (on by default; do networkTest: false to disable)
| Option | Type | Default | Description |
| :--- | :---: | :---: | :--- |
| testUrls | string[] | (built-in list) | Replaces the default URL list. Use your own ad/analytics URLs for best accuracy. |
| extraTestUrls | string[] | [] | Adds URLs to the default list without replacing it. |
| networkTestCount | number | 3 | How many URLs to randomly pick and test per check. |
| networkThreshold | number | 2 | Minimum number of blocked URLs required to trigger detection. |
| networkTimeout | number | 5000 | Milliseconds before a request is considered blocked (timeout). |
Banner Scan (only active if bannerScan: true)
| Option | Type | Default | Description |
| :--- | :---: | :---: | :--- |
| customBaitClasses | string[] | [] | Additional CSS classes for bait ads, e.g. form ads you serve. |
| baitCheckInterval | number | 100 | Interval in ms between each bait visibility check. |
| baitCheckAttempts | number | 5 | Max checks before the banner scan stops (def.interval(100ms) * def.attempts(5)= 500ms). |
Analytics
| Option | Type | Default | Description |
| :--- | :---: | :---: | :--- |
| gtag | boolean | false | If true and window.gtag is present, fires a GA4 event automatically on detection. |
| gtagEventName | string | "adblock_detected" | The GA4 event name (for dashboard). |
| gtagParams | object | {} | Extra parameters sent with the event, like { page_type: 'article' }. |
| analyticsCallback | function | null | Custom callback for any analytics provider (Plausible, Matomo, etc.). Receives (detected, reason, details). |
Callbacks
| Option | Type | Description |
| :--- | :---: | :--- |
| onDetected | function | called when a blocker is detected (no parameters) |
| onNotDetected | function | called when all tests pass (no blocker;no parameters) |
| onResult | function | always called after detection. Receives (detected: boolean, reason: string, details: object). |
API
const guard = new BlockGuard(options);
guard.check(); // Manually trigger a detection run. Returns Promise<boolean | null>
guard.reset(); // Clear results and allow re-running check(). Returns `this`.
guard.destroy(); // Remove all DOM elements and listeners. Use in React useEffect cleanup.
guard.onDetected(fn); // Add a detected callback. Returns `this` (chainable).
guard.onNotDetected(fn);// Add a not-detected callback. Returns `this` (chainable).
guard.getResult(); // Returns: true | false | null (null = not checked yet)
guard.getReason(); // Returns a detection reason string, like 'network_blocked'
guard.getDetails(); // Returns the full result object including per-URL network resultsDetection reasons
| Reason | Trigger |
| :--- | :--- |
| network_blocked | ≥ threshold URLs failed the network test |
| bait_blocked | an ad was hidden by CSS |
| bait_removed | an ad was removed |
| bait_modified | an ad's class/style was modified |
| all_passed | no blocking detec. |
Google Analytics (gtag)
If GA4 is initialized on your page (via Google Tag Manager or gtag.js snippet), setting gtag: true is enough, BlockGuard will call window.gtag('event', ...) automatically.
new BlockGuard({
gtag: true,
gtagEventName: 'adblock_detected', // default
gtagParams: { page_type: 'premium_article' } // optional extra params
});For other analytics providers (Plausible, Matomo, Fathom):
new BlockGuard({
analyticsCallback: function(detected, reason) {
if (detected) {
plausible('Adblock Detected', { props: { reason: reason } });
}
}
});(please check if this is correct for your provider, but i think all of them have similar approaches)
Integrations
mini projects and short guides are available in /integrations:
/integrations/vanilla-js— Plain HTML + CDN/integrations/nextjs-react— Next.js App Router/integrations/nuxt-vue— Nuxt/integrations/astro— Astro/integrations/wordpress— WordPress Plugin (the .zip file; with tutorial)
TypeScript
Full TypeScript support is included. No @types/ package needed.
import BlockGuard, { BlockGuardOptions } from 'ad-blockguard';
const options: BlockGuardOptions = {
networkThreshold: 2,
onDetected: () => console.log('Blocked!'),
};
const guard = new BlockGuard(options);The idea of BlockGuard was partly inspired from BlockAdBlock by sitexw, thanks ^^
Contributing
Pull requests are welcome! Please open an issue first to discuss what you would like to change, thanks. 😉🤝
License
MIT © 2026 Ponk445
