測定IDを保存し、トップページの閲覧がRealtimeに現れた。
これで設定は終わったように見える。
ところが、Next.jsの画面遷移を数えていなければ2ページ目以降が欠け、Google側とアプリ側の両方で遷移を数えていれば同じ閲覧が二つ並ぶ。
GA4の導入では、タグが読み込めたことと、正しいページ_viewが継続して届くことを分けて確かめる必要がある。
この記事は、Google Analytics 4のプロパティとウェブデータストリームを用意し、このブログの現行実装へMeasurement IDを設定して、Cookie同意とSPA遷移を検証する手順を説明する。
実装とGoogle公式資料は2026年7月26日に確認した。
掲載するIDはすべて例示用であり、実際のMeasurement ID、検証トークン、アカウント名は含めていない。
Cookie同意からGA4の読込、SPA遷移、RealtimeとDebugViewでの確認までを結ぶ流れ*図: 同意前はGoogle タグを読み込まず、同意後の初回表示とクライアント遷移を別々に計測し、送信結果をブラウザーとGA4の両側で照合する。*
プロパティ、データ ストリーム、Measurement IDを分ける
GA4の設定画面には、アカウント、プロパティ、データ ストリームという異なる単位がある。
この三つを同じ設定値として扱うと、管理画面でどの値をブログへ入れるのか分かりにくくなる。
プロパティは、レポート、イベント、利用者権限などをまとめる計測先である。
ウェブデータストリームは、そのプロパティへWebサイトのデータを送る入口である。
Measurement IDはウェブデータストリームに付くG-で始まる識別子であり、このブログへ設定する値はこれである。
Google AnalyticsのAdminで次の順に作成する。
- Analytics アカウントがなければ作成する。
- 対象サイト用のGA4 プロパティを作り、reporting time zoneとcurrencyを運用条件に合わせる。
- Adminのデータ StreamsからWebを選び、本番サイトのHTTPS URLと識別しやすいストリーム名を登録する。
- Web ストリーム detailsを開き、ストリーム detailsに表示されるMeasurement IDをコピーする。
この時点では、プロパティを作っただけではデータは届かない。
反対に、G-XXXXXXXXXXをコードへ書いただけでも、存在しないストリームや別プロパティのIDなら目的のレポートには届かない。
コピー元のプロパティ名とストリーム URLを画面上で確認してから、IDだけを設定画面へ移す。
このブログへMeasurement IDを保存する
現行ブログには、管理画面のマーケティング設定に「Google Analytics 測定ID」と「Analytics」の有効切替がある。
本番では保存済みのmarketing settingsを読み、保存値がない場合はNEXT_PUBLIC_GA_MEASUREMENT_IDをフォールバックとして使う。
そのため、通常の運用では管理画面へ例示形式のIDを入力し、Analyticsを有効にして保存すればよい。
RootLayoutはリクエストごとにmarketing settingsを読み、Analyticsが無効なら空文字列をGoogleAnalytics コンポーネントへ渡す。
コンポーネント側はG-から始まる形式を検証し、空または不正な値ならタグを読み込まない。
保存後は本番URLを新しいナビゲーションで開き、HTMLが表示されたという事実だけでなく、同意後のネットワーク リクエストまで確認する。
環境変数を使う場合も、リポジトリへ実値を書き込む必要はない。
.env.exampleのplaceholderと同じ形式でデプロイ環境へ設定し、ビルドログや記事のスクリーンショットへ実値を残さない。
Measurement IDはGoogle タグの送信先を選ぶ識別子だが、実値を記事へ転載する理由にはならない。
同意前にタグを読み込まない現行動作
このブログのGoogleAnalytics コンポーネントは、localStorageに保存されたmarketing同意をuseSyncExternalStoreで購読する。
利用者がまだ同意していない場合と拒否した場合は、googletagmanager.comの読み込みスクリプトもinline 設定も追加しない。
利用者が同意すると、コンポーネントは読み込みスクリプトと設定を一つずつ追加し、analytics_storageをgrantedへ更新してから計測を始める。
現行の順序は次のとおりである。
- 同意状態とMeasurement IDを検査する。
- 同意済みの場合だけGoogle タグの読み込みスクリプトを追加する。
- consentのdefaultとして
ad_storageとanalytics_storageをdeniedにする。 analytics_storageだけをgrantedへ更新する。jsとconfig コマンドを実行する。- 同意が撤回されたら両ストレージを
deniedへ更新し、スクリプト elementとその文書内のランタイムを除去する。
別タブで拒否へ変更された場合はストレージ イベントを受け取り、読み込み途中のoptional スクリプトを止めて文書を再読込する。
ただし、撤回より前にGoogleへ送信済みのイベントまでブラウザーから取り消せるわけではない。
撤回後の送信を止める動作と、すでに収集されたデータの管理は別の問題である。
このcleanupは既存のAnalytics cookieを削除しないため、Cookie削除やデータ deletionが必要な要件は別途実装する。
この実装を「Consent モード v2へ完全対応済み」と説明することはできない。
Googleの現在のconsent モードにはad_storageとanalytics_storageに加え、広告用user データのad_user_dataと広告personalizationのad_personalizationがある。
現行コンポーネントは後者二つを設定せず、拒否中はタグ自体を読み込まないため、Googleが区別するbasic consent モードに近いconsent-gated loadingである。
広告計測を含む完全なv2実装、地域別default、CMP連携、法的要件までを保証するものではない。
必要なconsent typeと表示文言は、利用するGoogle製品、配信地域、組織の方針を確認して別途設計する。
初回表示とSPA遷移の所有者を一つにする
通常の文書 読み込みでは、gtag('config', ...)が既定でページ_viewを送る。
一方、Next.jsのクライアント遷移ではHTML 文書を読み直さないため、初回読み込みだけを見ているタグでは次のURLが自動で数えられるとは限らない。
GoogleのSPAガイドは、履歴APIの変更またはカスタムイベントを使ってvirtual ページビューを発火し、各screen interactionとreferrerを確認するよう案内している。
現行コンポーネントはアプリ側でSPA遷移を所有する。
pushStateとreplaceStateをpatchし、popstateも購読して、pathname + searchが変わったときにgtag('config', measurementId, { page_path })を呼ぶ。
直前に送ったパスと同じ値は捨て、window.gtagの準備前に起きた遷移は最新パスだけを保留して送る。
初回パスはconfigの既定ページ_viewへ任せるため、同じ初回表示を手動送信しない。
現行コンポーネントが遷移時に明示するのはpage_pathだけであり、page_locationとpage_referrerを引数で更新しているわけではない。
Googleの推奨どおりvirtual referrerを維持できているかはDebugViewで確認し、最初の文書 referrerのままなら明示的なpage_view イベントと前のURLを保持する設計へ改める必要がある。
ここで、GA4 ウェブストリームのEnhanced measurementでも「ページ changes based on ブラウザー履歴イベント」を有効にすると、同じ履歴 APIの変更をGoogle側も検出し得る。
アプリ側のlistener、Enhanced measurement、Googleタグマネージャーの履歴 トリガーを重ねると、同じ遷移に複数のページ_viewが発生する。
送信経路は一つに決める。
現行コンポーネントを保つ場合は、ウェブストリームのEnhanced measurementでページビューsの詳細を開き、「ページ changes based on ブラウザー履歴イベント」を無効にして、クライアント遷移をコンポーネントだけに任せる。
Google側の自動計測へ統一する場合は、先にコンポーネントの履歴 listenerを削除し、Enhanced measurementのページ loadsとブラウザ 履歴 changesを有効にして検証する。
Googleタグマネージャーを採用する場合も、GA4の自動履歴計測とGTM トリガーを同時に有効にしない。
すべてのページ_viewを明示的なevent コマンドで送る別設計なら、初期configへsend_page_view: falseを指定し、初回も含めて一つの共通処理から送る方法がある。
現行実装はその方式ではない。
一般的なsnippetからsend_page_view: falseだけを持ち込むと、初回ページ_viewが消えるので注意する。
なお、現行のパスはクエリ stringを含むがhashを含まない。
Hashだけでscreenを切り替えるアプリへ流用するなら、現在のlistenerでは別ページとして扱われない。
このブログのような履歴 APIベースのルートでも、locale変更、クエリ付きURL、戻る、進むをそれぞれ試す必要がある。
URLとイベント parameterへ個人情報を入れない
Googleのポリシーは、Googleが個人を識別または認識できる情報をAnalyticsへ送らないよう求めている。
メールアドレス、個人の電話番号、氏名、問い合わせ本文などをイベント parameterへ入れるわけにはいかない。
画面に表示していない値でも、送信payloadへ含めれば同じである。
特にこのブログの現行コンポーネントはpathname + searchをpage_pathとして送る。
したがって、メールアドレス、電話番号、氏名、認証トークンなどをクエリ parameterへ置く設計は、GA4導入前から避けなければならない。
Google Analyticsのデータ redactionにはメール形式と指定クエリ parameterを除く機能があるが、Googleはbest effortと説明している。
redactionを、アプリ側で個人情報を送らない設計の代わりにはできない。
カスタムイベントにも同じ制約を適用する。
trackEventへ渡す値は、locale、buttonの種類、公開記事のslugなど、分析目的に必要な低cardinalityの値へ限定する。
検索窓の全文、contact formの内容、chat入力、自由記述エラー messageは送らない。
送信前のallowlistをコードで定めると、後から追加したUIが意図せず自由入力を流す事故を減らせる。
本番で一つのページ_viewを追う
ローカル環境でタグ elementを見つけただけでは、本番のCSP、同意状態、広告blocker、Measurement IDの取り違えを検出できない。
検証は、ブラウザー、DebugView、Realtimeの順に同じ操作を追う。
- 保存済み同意を消した新しいブラウザ profileで本番URLを開く。
- 同意前に
googletagmanager.com/gtag/jsとAnalyticsのcollect リクエストが送られていないことをNetwork パネルで確認する。 - 同意後に読み込みスクリプトが一つだけ追加され、Google タグへのリクエストが成功することを確認する。
- Google タグ Assistantまたはdebug モードを有効にし、GA4のAdminからDebugViewを開く。
- 最初のページ_viewを確認し、日本語記事、英語記事、クエリ付きURLへクライアント遷移する。
- 各遷移でページ_viewが一つだけ増え、
page_locationとpage_referrerが直前の画面に合わせて変わることを確認する。 - ブラウザの戻ると進むを使い、同じ規則で一つずつ増えることを確認する。
- 同意を撤回し、その後の遷移で新しいcollect リクエストが出ないことを確認する。
- Realtimeで直近5分と30分のactivityを確認する。
DebugViewはdebug モードを有効にしたdeviceのイベントをほぼリアルタイムで追う場所であり、通常レポートの代替ではない。
Realtimeにも一部のdimensionとuser acquisition処理に制約がある。
Googleのデータ freshness文書では、処理中の通常レポートは変化することがあり、データ processingに24時間から48時間かかる場合があると説明されている。
Realtimeにイベントがあり、通常レポートにすぐ現れないだけなら、設定失敗と断定せず翌日以降も確認する。
症状から送信経路を絞る
| 症状 | 確認する場所 | よくある原因 |
|---|
| 同意後もリクエストがない | 管理画面、Elements、Network | Analyticsが無効、ID形式が不正、同意が拒否、CSPまたは拡張機能によるblock |
| トップページだけ記録される | 履歴 API、DebugView | SPA遷移listenerが動いていない、hashだけが変わっている、別router方式を使っている |
| 遷移ごとに二件記録される | DebugViewのtimestampとparameter | コンポーネントとEnhanced measurement、またはGTMが同じ履歴 changeを送っている |
| 古いパスが後から届く | Networkとナビゲーション順序 | タグ準備前のqueueがstale、非同期処理が現在URLを再確認していない |
| DebugViewにdeviceが出ない | debug モード、同意、プロパティ | debug モードが無効、別プロパティのID、ブラウザ側でリクエストがblockされている |
| Realtimeにはあるがレポートにない | Realtime、データ freshness | 通常レポートが処理中で、24時間から48時間の範囲にある |
| URLに個人情報が見える | ページ_location、ページ_パス、クエリ | アプリが自由入力やトークンをURLへ含め、redaction前の値を送っている |
重複を調べるときは、GA4画面の合計値だけを眺めず、一回のクリックに対応するNetwork リクエストとDebugView イベントを時系列で並べる。
二件のparameterが同じなら二重送信を疑い、異なるならredirectや別画面の発火条件を調べる。
この切り分けなら、計測値の違和感を推測だけで直さずに済む。
Search ConsoleのクリックとGA4のセッションは同じ数にならない
Search ConsoleのPerformanceレポートは、Google検索結果でのimpression、クリック、CTR、average positionを扱う。
GA4は、サイトへ到着したあとにタグが送ったページ_view、イベント、セッション、user 挙動を扱う。
前者のクリックと後者のセッションは測定地点も成立条件も異なるため、一対一で一致する指標ではない。
このブログでは、同意前と拒否中にGA4 タグを読み込まない。
Search Consoleでクリックが数えられても、利用者が同意しない、JavaScriptがblockされる、到着前に離脱するなどの条件では対応するGA4 イベントがない場合がある。
日付境界、attribution、URLのgroupingも確認し、単純な差分を実装障害と決めつけない。
GA4のウェブデータストリームとSearch Consoleプロパティをlinkすると、Analytics内でオーガニック検索クエリとランディングページに関するレポートを利用できる。
それでもSearch Console metricsとAnalytics metricsの定義が同じになるわけではない。
Search Console側のプロパティ作成、所有権確認、sitemap送信は、Next.jsブログにGoogle Search Consoleを設定するで分けて説明している。
Google公式参考資料