🧱

Reactで実装するAdBlock検知 〜ユーザ体験とwebサイト収益の両立のために〜

に公開

はじめに

web広告は、無料でウェブサービスを運営するために不可欠な財源です。しかし近年、過剰な広告表示によるユーザ体験の悪化を背景に、多くのユーザが AdBlock拡張機能 や広告ブロック機能付きブラウザを利用するようになりました(参考)。

その結果、広告収益に依存するウェブサイトにとっては 収益減少のリスク が高まり、運営を持続させる上で AdBlockの検知と適切な対処 が重要になりつつあります。

本記事では、Reactプロジェクトで実際に試した AdBlock検知の手法と、検知後のUI実装 について整理します。

AdBlockの仕組みをざっくり整理

広告ブロッカーにはいくつかの方式があります(参考)。

  • DNSフィルタリング:広告配信ドメイン自体への通信を遮断
  • URLフィルタリング:特定のスクリプトやリソースのURLをブロック
  • コンテンツフィルタリング / CSSインジェクション:広告要素を検出し、削除または非表示にする
  • JSインジェクション:広告スクリプトの実行を阻止

そのため「検知」するには、要素の非表示状態やリクエストエラーを利用するのが一般的です。

Reactでの検知方法

1. リクエスト方式

既知の広告スクリプトを fetch() し、失敗すればAdBlock動作中と判断(参考)。

const [adBlockDetectedFromFetch, setAdBlockDetectedFromFetch] =
    useState(false);

useEffect(() => {
    fetch(
        "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js",
        {
            method: "HEAD",
            mode: "no-cors",
            cache: "no-store",
        },
    )
        .then()
        .catch((_) => {
            console.log(
                "Cannot load adsbygoogle.js. Adblock may be active. ",
            );
            setAdBlockDetectedFromFetch(true);
        });
}, []);

2. 要素の表示状態検知

広告要素のスタイルが上書きされていないかを確認する手法。

function useElementVisibility(selector: string) {
    const [isPresent, setIsPresent] = useState(false);
    const [isVisible, setIsVisible] = useState(false);

    useEffect(() => {
        const checkElement = () => {
            //要素の存在を確認
            const el = document.querySelector(selector) as HTMLElement | null;
            setIsPresent(!!el);

            if (el) {
                //要素が存在するなら、そのスタイルを確認
                const style = window.getComputedStyle(el);
                const rect = el.getBoundingClientRect();

                const visible =
                    style.display !== "none" &&
                    style.visibility !== "hidden" &&
                    parseFloat(style.opacity) > 0 &&
                    rect.width > 0 &&
                    rect.height > 0;

                setIsVisible(visible);
            } else {
                setIsVisible(false);
            }
        };

        // 初回チェック
        checkElement();

        // MutationObserverでDOM改変を監視
        const observer = new MutationObserver(checkElement);
        observer.observe(document.body, {
            childList: true,
            subtree: true,
            attributes: true, // style や class 変更も検知
        });

        // IntersectionObserverで実表示を監視
        let intersectionObserver: IntersectionObserver | null = null;
        const target = document.querySelector(selector);
        if (target) {
            intersectionObserver = new IntersectionObserver((entries) => {
                entries.forEach((entry) => {
                    if (entry.target === target) {
                        setIsVisible(entry.isIntersecting);
                    }
                });
            });
            intersectionObserver.observe(target);
        }

        // resize / scroll / visibilitychange でも再チェック
        window.addEventListener("resize", checkElement);
        window.addEventListener("scroll", checkElement);
        document.addEventListener("visibilitychange", checkElement);

        return () => {
            observer.disconnect();
            if (intersectionObserver) intersectionObserver.disconnect();
            window.removeEventListener("resize", checkElement);
            window.removeEventListener("scroll", checkElement);
            document.removeEventListener("visibilitychange", checkElement);
        };
    }, [selector]);

    return { isPresent, isVisible };
}

3. ライブラリ利用

adblock-detect-react のようなライブラリを利用する手もありますが、ブロッカー側に検知されやすいため、自作判定との併用 が安全です。

実際に検証した環境

主要なAdBlock環境での検知性能を検証しました。

  • AdBlock組み込み型ブラウザ
    • Brave
    • Vivaldi
  • Chrome拡張
    • AdBlock
    • uBlock Origin
    • AdGuard
    • Ghostery
    • Privacy Badger
    • AdBlock Plus
    • AdBlocker Ultimate
  • Firefox拡張
    • AdBlock for Firefox
    • Adlock
  • Safari拡張
    • AdGuard for Safari

紹介した3つの方法はそれぞれ検知できない環境がありましたが、組み合わせることで上記の環境全てで検知に成功しました(2025.08現在)

検知後のUI設計

AdBlockを検知した後の表示には以下のようなものがあります。

モーダル表示

全画面を覆うモーダルで「AdBlockを解除してください」と表示する方式。

確実に目に入りますが、ユーザ離脱のリスクも大きいため、利用は慎重に。

バナー表示

ページ上部や広告枠部分に「このサイトは広告収益で運営しています」と表示。

一度は目に入るが、ユーザ体験を阻害しにくいのがメリット。

{blocked && (
  <div className="fixed bottom-0 w-full bg-yellow-200 p-3 text-center shadow-md">
    🚫 AdBlockが有効です。このサイトをサポートするため広告解除をご検討ください。
  </div>
)}

公式機能の活用

Google Adsense には公式に「AdBlock検知・通知機能」が存在します。

https://support.google.com/adsense/answer/11576085?hl=ja

可能であればまずは公式手段を活用するのが安全です。

検証環境の構築

多くのweb開発において、本番環境にデプロイする前にローカルで検証することが多いと思います。しかしながら、Google広告、たとえばGoogle Adsenseはそのままだと登録済みのURLにしか広告を配信してくれません。そこで、ローカル環境で広告を検証するには以下の準備が必要になります。ここではGoogle Adsenseでの方法を書きます.

  • /etc/hostsを編集し、localhostのaliasとしてdev.<本番環境ドメイン>.comを追加する。
  • Adsenseのタグにdata-adtest="on"を追加する。これによって収益に反映されないテスト用広告が配信されます。
<ins 
  className="adsbygoogle"
  data-ad-client={登録済みのクライアントID}
  data-ad-slot={登録済みのAD SLOT}
  data-ad-format="auto"
  data-adtest="on" // <- これ!
/>

参考
https://eight-bites.blog/2021/11/adsense-localhost/
https://support.google.com/adsense/answer/9055049?hl=ja

ユーザ体験を損なわない工夫

  • 強制ブロックは避ける

    YouTubeのような「広告ブロック解除 or Premium必須」方式は、巨大で多くのユーザが依存しているサービスでない限りユーザ離脱を招き逆効果になるかもしれません。

  • 1度だけ訴求する

    モーダルのような画面占有率の高いメッセージを何度も出すのは不快要因になりやすいため、CookieやLocalStorageで制御(参考)

  • 透明性の確保

    「広告収益で運営しているためご協力ください」と伝える方が、ユーザ理解を得やすい。

今後の展望

  • サーバーサイド広告挿入(SSAI):動画配信で広がる技術。クライアントでのブロックは困難。
  • Acceptable Ads対応:AdBlock Plusなど一部は「許容可能な広告」枠を用意しており、規格準拠すれば表示される可能性もある。
  • 課金・寄付モデルとの併用:The Guardian や ニコニコ動画のように、広告以外の収益源を提示するのも重要。

まとめ

  • AdBlockはユーザにとって利便性がある一方、サイト運営にとっては死活問題。
  • Reactでは、要素の表示状態チェック方式・リクエスト方式・ライブラリ利用を組み合わせることで多くの環境で検知が可能。
  • 検知後は、モーダルよりもバナー強制よりも依頼 の方がユーザ体験を損なわない。
  • 公式機能やサーバーサイド手法、寄付モデルなども検討できる。
Emoba Tech Blog

Discussion