- Published on
如何优雅判断 AdBlock 是否开启?前端实现方案分享
- Authors

- 作者
- kai
目录

背景:海外项目遭遇检测失效危机
2个月前,负责海外用户项目时突然发现:原本稳定运行的AdBlock/AdBlock Plus检测功能全部失效。大量海外用户因广告拦截器被误判(或漏判),导致核心功能体验异常。
排查后发现,AdBlock/AdBlock Plus疑似进行了规则升级(或过滤规则调整),直接导致传统检测逻辑失效。作为面向海外用户的项目,广告拦截器的准确检测对业务体验至关重要,因此专门针对这一问题进行了深入研究,最终找到了解决方案。
一、失效的老方法:为何突然不管用了?
此前我们一直使用「请求含/ad路径接口」的方式检测,核心逻辑如下:
// 旧检测逻辑:通过请求含/ad路径接口判断
function checkAdBlock() {
// 请求含/ad的路径(曾被多数广告拦截器识别)
fetch('xxxx/xxxx/ad')
.then((response) => {
if (!response.ok) {
console.log('AdBlock 可能已启用,请求被拦截')
} else {
console.log('AdBlock 未启用,请求成功')
}
})
.catch((error) => {
console.log('AdBlock 可能已启用,请求失败', error)
})
}
checkAdBlock()
失效原因:后续分析发现,AdBlock/AdBlock Plus的核心过滤规则集(EasyList)已移除对单纯/ad路径的拦截规则,导致这类请求不再被拦截,旧方法自然失效。
二、无效的尝试:主流方案均已失效
为解决问题,我们测试了目前开源社区主流的检测工具,结果全部失效:
- FuckAdBlock:基于旧版规则设计,无法适配最新过滤逻辑
- adblockDetector:依赖特定DOM类名检测(如
ad/ads),现已被规则规避 - AdBlock Warning:核心拦截路径规则已过时
- AdGuard Detector:针对AdGuard优化,对AdBlock/AdBlock Plus适配失效
- blockadblock:商业化方案更新滞后,未跟进规则变化
三、另一种无效思路:基于DOM类名的检测
除了开源工具,我们还尝试了「创建含敏感类名的DOM元素」的方法,同样无效:
// 尝试通过DOM类名检测(已失效)
var adTest = document.createElement('div')
adTest.className = 'ad ads ad-test1 adblock-test' // 曾被拦截的敏感类名
adTest.style.display = 'block'
adTest.style.position = 'absolute'
adTest.style.top = '-9999px'
adTest.style.left = '-9999px'
adTest.style.width = '1px'
adTest.style.height = '1px'
document.body.appendChild(adTest)
// 检测元素是否被隐藏(结果:始终未被拦截)
var adblockActive =
(adTest.offsetWidth <= 0 && adTest.offsetHeight <= 0) || adTest.style.display === 'none'
if (adblockActive) {
console.log('检测到AdBlock开启了')
} else {
console.log('AdBlock没有开启')
document.body.removeChild(adTest)
}
失效原因:最新规则已不再通过类名直接拦截DOM元素,而是转向对「资源路径」和「脚本特征」的识别。
四、突破口:从EasyList规则找答案
既然传统方法失效,我们决定从源头分析——AdBlock/AdBlock Plus的核心过滤规则集「EasyList」。
什么是EasyList?
EasyList 是开源的广告过滤规则集(由社区维护),是AdBlock、AdBlock Plus、uBlock Origin等工具的默认规则来源。其规则会定期更新,直接决定广告拦截器的拦截逻辑。
通过分析最新版EasyList规则文件(文件较大,建议按需查阅),我们发现了关键的脚本拦截规则,例如:
! *** easylist:easylist/easylist_general_block.txt ***
-ad-manager/$~stylesheet
-ad-sidebar.$image
.ads.controller.js$script
这些规则的含义是:
-ad-manager/$~stylesheet:拦截路径含-ad-manager/的资源(排除样式表)- 例:
https://example.com/ad-manager/script.js会被拦截,style.css不会
- 例:
-ad-sidebar.$image:拦截路径含-ad-sidebar的图片资源- 例:
https://example.com/ad-sidebar/banner.jpg会被拦截
- 例:
.ads.controller.js$script:拦截路径含.ads.controller.js的脚本- 例:
https://example.com/xxx.ads.controller.js会被拦截
- 例:
五、有效方案:基于EasyList规则的检测实现
根据上述规则,我们设计了两种验证方案,均测试有效:
方案1:检测-ad-manager/路径脚本
<script>
// 创建测试脚本,路径含-ad-manager/(符合EasyList拦截规则)
var script = document.createElement('script')
script.src = './a-ad-manager/test.js' // 本地需存在该路径文件(内容可空)
script.onload = function () {
console.log('脚本加载成功,AdBlock未启用')
}
script.onerror = function () {
console.log('脚本加载失败,AdBlock已启用(被拦截)')
}
document.head.appendChild(script)
</script>
方案2:检测.ads.controller.js脚本
<script>
// 创建测试脚本,文件名含.ads.controller.js(符合EasyList拦截规则)
var script = document.createElement('script')
script.src = './test.ads.controller.js' // 本地需存在该文件(内容可空)
script.onload = function () {
console.log('脚本加载成功,AdBlock未启用')
}
script.onerror = function () {
console.log('脚本加载失败,AdBlock已启用(被拦截)')
}
document.head.appendChild(script)
</script>
六、总结与建议
- 核心原理:AdBlock/AdBlock Plus的拦截逻辑完全依赖EasyList规则,检测需基于最新规则设计(避免使用已被移除的旧规则特征)。
- 稳定性建议:
- 定期查阅EasyList更新日志,及时适配规则变化
- 生产环境可同时使用多种规则检测(如上述两种方案),提高准确率
- 避免过度依赖单一规则(规则可能被临时调整)
- 适用场景:该方案对海外主流广告拦截器(AdBlock、AdBlock Plus、uBlock Origin)均有效,尤其适合海外用户项目。
通过以上方法,我们已在生产环境稳定运行很久,检测准确率恢复至99%以上,解决了规则升级导致的失效问题。
