analytics.jsのブロックを検出する

はじめに

GoogleAnalyticsをブロックされている場合、問題が発生することがあります。ブロックされた場合、機能制限をかけるためにブロックを検出する処理について考えます。

ブロックパターン

  • analytics.js/tags.jsの読み込みブロック
  • Measurement Protocolの通信ブロック
    • https://www.google-analytics.com/collectへの通信をブロック
  • 公式のオプトアウト機能を利用したブロック

※主なブロックフィルター
 EasyPrivacy

判定する

analytics.jsの読み込み判定

function isloadedGoogleAnalytics() {
  // GAオブジェクトが存在する && 初期化済み
  return window.ga && ga.loaded;
}

この方法は、analytics.jsの読み込みを判定します。

ga.createの有無で判定する方法もある。
ga.qの有無で判定する方法もある。

Measurement Protocolの通信ブロック

GoogleAnalyticsのブロックを判定します。GoogleAnalyticsの上位APIであるMeasurement Protocolを使用してデータ収集を試み、データ収集の成功失敗を元にしてブロックを判定します。成功の判定は、GIFファイルの応答を元に確認します。

// GoogleAnalyticsのブロックを判定する
function canGoogleAnalytics(async) {
  async = async === true;

  const gif = [71,73,70,56,57,97,1,0,1,0,128,255,0,255,255,255,0,0,0,44,0,0,0,0,1,0,1,0,0,2,2,68,1,0,59];
  const can = function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.responseType = async ? 'arraybuffer' : '';
    xhr.open('POST', 'https://www.google-analytics.com/collect', async);
    if (async) {
      xhr.onload = function() {
        const array = new Uint8Array(xhr.response);
        resolve((200 <= xhr.status && xhr.status < 300 || xhr.status === 304)
             &&  gif.length == array.length
             &&  gif.toString() == array.toString());
      };
      xhr.onabort = function() { resolve(false); };
      xhr.onerror = function() { resolve(false); };
      xhr.ontimeout = function() { resolve(false); };
      try {
        xhr.send(null);
      } catch (e) { resolve(false); }
    } else {
      try {
        xhr.send(null);

        const array = Uint16Array.from(xhr.responseText.split(''), c => c.charCodeAt(0));
        return (200 <= xhr.status && xhr.status < 300 || xhr.status === 304)
            &&  gif.length == array.length
            &&  gif.slice(0, 10).toString() == array.slice(0, 10).toString();
        // 補足:データの一部が`�`(65533)に置換されてしまうため、長さと前方のみ確認する。
        //       非同期処理を使用することを推奨する。
      } catch (e) { return false; }
    }
  };
  return async ? new Promise(can) : can();
};

console.log('async', await canGoogleAnalytics(true));
console.log('sync', canGoogleAnalytics(false));

※非同期処理の使用を推奨します。
※GIFファイル応答の仕様に依存する。
※GIFファイル応答まで偽装された場合は、すり抜ける。

公式のオプトアウト機能を利用したブロック

公式のオプトアウト機能の判定方法は、不明です。上記の方法で判定できないことまでは確認しましたが、判定する方法を発見することはできませんでした。

備考

hitCallbackによる判定

ga('send', 'event', 'outbound', 'click', url, {
  'transport': 'beacon',
  'hitCallback': function() { console.log('send'); }
});

hitCallbackは、ヒットの送信が完了したタイミングで通知されます。そのため、通知を待機することでデータ収集を確認できそうです。実際には、データを収集できません。ですが、コールバック関数はコールされます。これは、通信遮断による問題です。送信は完了しましたが、データはサーバに到達しなかっただけの問題です。

/debug/collect

  • https://www.google-analytics.com/debug/collect

上記のURLを使用することもできます。テスト用のAPIでJSONを応答します。ただし、パスが/debug/collectになるため、判定をすり抜ける可能性があります。

GoogleAnalyticsでのブロック回避

お客様は本サービスに含まれるプライバシー機能(オプトアウトなど)を一切回避してはなりません。


上記の通り、GoogleAnalyticsではブロック回避をすることは禁止されています。ブロックを回避してデータを収集してはいけません。ただし、ページの機能制限まで禁止されているわけではありません。

Googlebot

Googlebotは、GoogleAnalyticsをブロックします。GoogleAnalytics無効時に単純にコンテンツを非表示等にするとGoogle検索からサイトが消える可能性があります。確認には次の記事が参考になります。

参考