navigator.webdriver が true のブラウザでは計測そのものを始めないようにしました。ただ、修正を入れた後の約5時間でも米国のセッションは減っておらず、この判定だけでは止められないBotが残っています。
お疲れ様です!IT業界で働くアライグマです!
個人開発している技術ニュースキュレーションサービス「DevPick」のGA4を見ていたところ、米国からのセッションだけが急に増えていました。日本語中心のサービスなので海外からの閲覧は少ないはずで、増え方も不自然でした。
この記事では、そのセッションをBotと判断した根拠、GA4の設定だけでは除外できなかった理由、コードで計測を止めた方法、そして修正後もまだ残っている分を順に書きます。
米国からのセッションだけが急に増えていた
1日1〜2件だった米国のセッションが、30件を超えた
DevPickの閲覧者はほとんどが日本からです。米国からのセッションは、10月中旬までは1日1〜2件で、0件の日もありました。それが10月23日に17件、10月27日には34件になっていました。
GA4のData APIで、米国のセッション数と平均セッション時間を日ごとに取り出したのが次の表です。
| 日付 | 米国のセッション | 平均セッション時間 |
|---|---|---|
| 10月16日 | 2 | 約1秒 |
| 10月18日 | 2 | 約3秒 |
| 10月20日 | 4 | 約62秒 |
| 10月21日 | 6 | 約2秒 |
| 10月22日 | 4 | 約52秒 |
| 10月23日 | 17 | 約5秒 |
| 10月24日 | 10 | 約2秒 |
| 10月25日 | 11 | 約2秒 |
| 10月27日 | 34 | 約5秒 |
10月26日は米国からのセッションが記録されていませんでした。増え方は一直線ではなく、日によってばらつきがあります。
全体の数字を押し上げるほどの量になっていた
同じ時期のサイト全体のセッションは、1日20〜60件ほどです。10月27日は全体が64件で、そのうち34件が米国でした。半分以上が、日本語のサービスを数秒で離れていく米国からのセッションだったことになります。
この状態では、ユーザー数やセッション数が増えても、利用者が増えたのか、Botが増えたのかを区別できません。平均のエンゲージメント時間のように、全体を平均する数字も下に引っ張られます。
IT女子 アラ美Botと判断した根拠 ─ 流入元・滞在時間・都市・画面サイズ
ほぼ全部がdirect流入の新規ユーザーで、滞在は約8秒だった
調べた時点(10月28日)の直近14日間で、米国からのセッションは101件ありました。そのうち94件は流入元が (direct) / (none) で、検索から来た google / organic は6件だけです。direct は、ブックマークやURLの直接入力のように「どこから来たか分からない」流入です。
平均滞在は約8秒で、日本からのセッションの約168秒とは大きく違います。101件のうち新規ユーザーが100件で、2回目以降の訪問は1件だけでした。直接URLを開いて数秒で離れ、二度と戻ってこない閲覧が、毎日まとまって来ていたことになります。
都市名にデータセンターの所在地が並んでいた
都市の内訳には、The Dalles、Ashburn、Boardmanが並んでいました。The DallesにはGoogleのデータセンターがあり、AshburnとBoardmanはAWSのデータセンターがある場所です。人が住んでいる街からの閲覧ではなく、クラウド上のサーバーからアクセスされていると考えるのが自然です。都市が空欄や (not set) のセッションも50件ありました。
画面サイズと時間帯も、人の閲覧らしくなかった
端末は Windows の Chrome が84件でした。画面サイズには 800×600、1280×1280、1024×1024 のような、普通のノートPCやモニターではあまり見ない値が含まれています。正方形の画面のように、人が使う端末としては不自然なものもありました。
アクセスの時間帯も、24時間に均等に散らばっていました。人の閲覧なら、住んでいる地域の昼や夜に偏るはずです。
ここまでの特徴から、このセッションは「JavaScriptを実行できるヘッドレスブラウザが、データセンターからページを開いている」ものだと判断しました。



GA4の設定だけでは除外できなかった
既知Bot除外は、JavaScriptを実行するヘッドレスブラウザを拾えない
GA4には、既知のBotやクローラーによるトラフィックを自動で除外する仕組みがあります。Googleアナリティクスのヘルプによると、判定はGoogleの調査と、IAB(Interactive Advertising Bureau)が管理する「International Spiders and Bots List」を組み合わせて行われます。この除外はオフにできず、除外された量も確認できません。
この仕組みが見ているのは、名乗り方が分かっているBotです。普通のChromeと同じようにページを開いてJavaScriptを実行し、GA4の計測タグも動かすヘッドレスブラウザは、ブラウザとしては普通の閲覧と見分けがつきません。実際、今回のセッションは除外されずにレポートに残っていました。
データフィルタには、国や都市で外す種類がなかった
次に、管理画面のデータフィルタで外せないかを確認しました。GA4のデータフィルタで作れるのは、次の3種類です。
- デベロッパートラフィック:デバッグモードで送られたデータを外す
- 内部トラフィック:指定したIPアドレスからのアクセスを外す
- ウェブホスト名のトラフィック:データを送ってきたドメインで外す
国や都市を条件にしたフィルタはありません。内部トラフィックのフィルタはIPアドレスを指定して外せますが、自分や社内からのアクセスを外すためのもので、外部から来るBotのIPアドレスを把握して登録する仕組みではありません。
レポートを見るときに国や流入元で絞り込むことはできますが、それはデータを集めた後の見方の話です。集まる数字そのものは水増しされたままなので、送る側で止めることにしました。



navigator.webdriverがtrueなら、gtagを初期化しない
navigator.webdriverは「自動操作されているか」をページに伝える値
ブラウザには navigator.webdriver という読み取り専用の値があります。MDNの説明では、ブラウザが自動操作で制御されているかどうかを示す値です。Chromeでは、--enable-automation や --headless を付けて起動したときなどに true になります。
自動操作に協力的なブラウザが「今は機械が動かしています」とページに自己申告する仕組み、と考えると分かりやすいです。この値が true のときは、GA4に何も送らないことにしました。
計測タグの初期化スクリプトの中で判定した
DevPickはNext.jsで作っていて、GA4の計測タグはNext.jsの Script コンポーネントに渡した初期化スクリプトで動かしています。navigator.webdriver はブラウザの中でしか読めないため、判定はこの初期化スクリプトの中に置きました。修正後のスクリプトを作る関数は次のとおりです。
export function gtagInitScript(gaId: string, rssLanding: boolean): string {
const campaign = rssLanding ? ",{campaign_source:'rss',campaign_medium:'referral'}" : "";
return `if(!navigator.webdriver){window.dataLayer=window.dataLayer||[];window.gtag=function(){dataLayer.push(arguments);};gtag('js',new Date());gtag('config','${gaId}'${campaign});}`;
}
修正前は、同じ処理を if で囲まずにそのまま書いていました。gtag('config', ...) を呼ぶとGA4はページビューを送ります。if(!navigator.webdriver) で囲んだことで、自動操作のブラウザではこの呼び出しが行われません。campaign_source の部分は、RSSから来た閲覧を見分けるための既存の処理です(経緯はRSSのutm_sourceで検索流入を誤計上しかけた話に書いています)。
gtagを定義しないことで、ほかのイベント送信もまとめて止まる
この修正では、gtag 関数の定義そのものも if の中に入れました。DevPickでは、ページビュー以外にもGA4へ送っているイベントがあります。表示速度の計測、広告枠のクリック、アプリの案内のクリックです。これらはどれも、送る前に gtag が使えるかを確かめています。
if (typeof gtag === "function") {
// GA4へイベントを送る
}
自動操作のブラウザでは gtag が定義されないので、この判定で全部止まります。イベントごとに navigator.webdriver を確かめる処理を足す必要はありませんでした。表示速度の計測についてはNext.jsでLCP・CLS・INPをGA4に送る話で書いていて、こちらの数字もBotで汚れずに済みます。
なお、GA4のライブラリ本体(gtag/js)の読み込みは、この修正では変えていません。止めたのは初期化と送信です。
テストは3通り、E2Eには影響しなかった
単体テストでは、関数が返す初期化スクリプトの文字列をブラウザと同じように実行し、navigator.webdriver が true・false・未定義の3通りで結果を確かめています。true のときは gtag もデータの送信キューも作られないこと、false と未定義のときは従来どおり送られることを確認しました。
一方で、E2Eテストに使っているPlaywrightのブラウザも navigator.webdriver が true になります。ただ、計測を確かめるE2Eテストは window.gtag を自前の関数に差し替えて送信内容を記録する作りだったため、この修正の影響は受けませんでした。



修正後の約5時間でも、米国のセッションは減らなかった
修正は10月28日21時14分に本番へ出た
この修正をmainにマージしたのは10月28日の21時01分で、自動デプロイが終わったのは21時14分です。本番のトップページが読み込むJavaScriptに if(!navigator.webdriver) の判定が入っていることも、配信されているファイルで確かめました。
翌朝8時過ぎにGA4のData APIで米国のセッションを分単位で取り出したところ、取得できたのは10月29日1時台までのデータでした。修正後の21時14分から1時台までに記録された米国のセッションは、次の8件です。
| 時刻 | 都市 | OS・画面サイズ | 流入元 |
|---|---|---|---|
| 21:31 | The Dalles | Linux・1280×1280 | direct |
| 21:47 | (空欄) | Windows・1366×768 | direct |
| 22:41 | The Dalles | Linux・1280×1280 | direct |
| 23:07 | (空欄) | Windows・1920×1080 | direct |
| 23:43 | Elizabeth | Windows・1920×1080 | direct |
| 0:22 | (空欄) | Windows・1920×1080 | direct |
| 0:36 | Providence | Mac・1512×982 | 検索 |
| 1:22 | (空欄) | Windows・1536×864 | direct |
前日の同じ時間帯より、むしろ多かった
前日の同じ時間帯(10月27日21時〜28日1時台)の米国のセッションは6件でした。件数で見る限り、修正の前後で減ってはいません。0時36分の検索から来たMacのセッションを除くと、残りの7件はどれも滞在が十数秒以下のdirect流入です。The Dalles・Linux・1280×1280という、修正前にも見ていた組み合わせも2件残っていました。
なぜ止まらなかったのかは、特定できていない
この7件が navigator.webdriver を false に偽装したBotなのか、そもそも true にならない方法で自動操作しているのかは、GA4のデータからは分かりません。GA4に届いているということは、少なくともこの判定を通り抜けたということです。修正を入れる時点で、webdriverを偽装するBotは残るので、レポートを見るときに国や流入元で絞って補う前提にしていました。それでも、修正前と比べて減ったと言える数字は、今のところ出ていません。
5時間分、件数にして1桁のデータなので、たまたまの揺れの可能性もあります。ただ、少なくともこの判定だけでBotの水増しが止まるとは言えない、というのが今の結果です。



まとめ
DevPickのGA4で急に増えた米国のセッションは、流入元・滞在時間・都市・画面サイズ・時間帯のどれを見ても、データセンターから来るヘッドレスブラウザの特徴と一致していました。GA4の既知Bot除外はJavaScriptを実行するブラウザを拾えず、データフィルタにも国や都市で外す種類がないため、GA4の設定だけでは除外できません。
そこで、navigator.webdriver が true のブラウザでは gtag の定義と初期化をしないようにしました。送信前に gtag の有無を確かめる作りにしていたので、ページビューだけでなく、表示速度やクリックのイベントもこの1か所の変更でまとめて止まります。
ただ、修正後の約5時間では米国のセッションは前日の同じ時間帯より減っておらず、同じ型のBotも残っていました。navigator.webdriver は自動操作に協力的なブラウザの自己申告なので、それを出さないBotには効きません。今の自分は、この判定を「正直に名乗る自動操作を外すための最低限の対策」と捉えています。数字を読むときは、国や流入元で絞って見ることを前提にしておくのが現実的だと考えています。












