GA4でランディングページの「その後」を分析する

ランディングページ起点の閲覧順別ページ分布を示す分析レポート画面

Googleアナリティクスでランディングページを軸に分析するとき、そのページのセッション数やコンバージョン、エンゲージメント率だけでなく、「そのページから始まった人がその後どのページを見ているのか」を確認したいことがあります。

特に知りたいのは、2ページ目にどのページが見られているのか、その後の行動ではどのようなページが見られているのか、という点です。

そこで、GA4の page_view に「セッション内で何ページ目の閲覧か」と「そのセッションのランディングページ」の情報を追加し、可視化・分析できるようにしました。

この記事では、その考え方と実装、Webサイト改善への活用方法を紹介します。

目次

知りたいのは「完全な閲覧経路」とは限らない

ページ間の移動を分析しようとすると、以下のように個々のユーザーの閲覧経路を正確に把握したくなるかもしれません。

ページA → ページB → ページC → ページD

目的によっては詳細なパス分析が必要なケースもあります。しかし、Webサイトの改善を目的にするのであれば、必ずしもさまざまなユーザーの閲覧経路を正確に把握する必要はありません。

例えばあるページをランディングページとしたセッションについて、以下のような状況を把握できればそれだけでも改善のための仮説を立てられます。

  • 2ページ目では問い合わせページが多い
  • 料金ページは2ページ目ではそれほど多くないが、3ページ目以降では頻繁に閲覧されている
  • 事例ページや会社概要など、特定のページが後続閲覧で繰り返し現れる

知りたいのは、一人ひとりが通った完全なルートというよりも「特定のランディングページを起点としたときに、どのページが、何ページ目で、どの程度閲覧されているのか」という分布です。

今回の取り組みでは、この「ランディングページ起点の閲覧順別ページ分布」を把握できることが着地点です。厳密なページ遷移の復元ではなく、改善判断に必要な解像度でその後の閲覧状況を把握するという考え方です。

page_viewに「起点」と「閲覧順」を追加する

計測面では、GA4の page_view イベントに、独自のイベントパラメータを2つ追加しました(カスタムディメンションの設定も必要です)。

  • session_page_view_index:そのページビューがセッション内で何ページ目の閲覧にあたるか
  • session_landing_page_location:そのセッションの1ページ目にあたるページ

例えば、次のような一連の閲覧があったとします。

/service
/price
/case
/contact

それぞれの page_view に、次のような情報を持たせます。

/service   1ページ目   LP=/service
/price     2ページ目   LP=/service
/case      3ページ目   LP=/service
/contact   4ページ目   LP=/service

各 page_view に「このセッションはどのページから始まったのか」と「これは何ページ目の閲覧なのか」という情報をイベントパラメータで付与しています。

これにより「特定のランディングページから始まったセッションの2ページ目」「同じセッションの3ページ目以降」といった形で、閲覧ページを分析できるようになります。

GTMとCookieでセッション内の情報を保持する

実装では、GoogleタグマネージャーのカスタムHTMLタグとCookieを利用しています。

セッション内ページ閲覧順を保持するCookieとランディングページを保持するCookieを用意し、ページが表示されるたびに閲覧順を更新します。同一セッションとして扱っている間はランディングページを維持し、セッションタイムアウトを超えた場合は新しいセッションとして閲覧順を1から開始します。ランディングページにはホスト名とパスを組み合わせた値を保存しています。

この処理を行うカスタムHTMLタグを page_view を送信するタグの直前に実行し、取得した閲覧順とランディングページの値をその後に送信される page_view のイベントパラメータとしてGA4へ渡します。GA4側では、この2つのイベントパラメータに対応するイベントスコープのカスタムディメンションを登録します。

具体的なカスタムHTMLのコードは記事末尾に掲載します(「付録:GTMの実装コード」の章を参照)。

Googleデータポータルでランディングページの「その後」を見る

取得したデータは、Googleデータポータルでレポートとしてまとめることができます。ランディングページを選択することで、そのページを起点とするセッションの閲覧状況が表示されます。

ランディングページ起点の閲覧順別ページ分布を示す分析レポート画面
レポート「ページ閲覧フロー」。セッションのランディングページを起点に、2ページ目以降でどのページが閲覧されたかを閲覧順別に確認できる

レポートの中央部に配置しているヒートマップ形式の表は、縦軸を閲覧ページ、横軸をセッション内の閲覧順としたデータです(グラフの種類は「ピボットテーブル」)。以下のような内容を把握できます。

  • 2ページ目ではどのページが多く閲覧されているか
  • 3ページ目以降で閲覧状況がどう変わるか、どのページが多く現れるか

Googleデータポータルのコントロール要素を利用して、デバイスカテゴリやセッションのデフォルトチャネルグループなどでも絞り込めます。

ここで把握しているのは個々のユーザーの実際のページ遷移を完全に復元したものではなく、ランディングページを起点とした閲覧順別のページ分布です。複数タブでの閲覧などでは実際の閲覧順とずれる場合もあるため、その前提で分析します。

いちしま泰樹

「ユーザーのページ閲覧経路を把握したい」という要望をよくいただきますが、把握したいのは実はこのようなデータではないでしょうか? 探索の「経路データ探索」で感じる「結局よくわからない」を解決します

2ページ目からランディングページを評価する

この分析でまず確認したいのは「2ページ目」です。ランディングページの後、2ページ目としてどのページが計測されているかを把握できます。

2ページ目の閲覧ページ階層別表示回数と構成比を示す集計データ表
「2ページ目の閲覧ページ」のみの表を準備すると把握しやすい

例えば、サービス紹介ページから問い合わせページへの移動を想定しているのであれば、問い合わせページが2ページ目として多く閲覧されているかを確認します。意図したページが多く閲覧されていれば、設計した次の行動につながっている可能性があります。

一方、想定していなかったページが多く閲覧されている場合は、その意味を考える必要があります。会社概要の閲覧が多ければ、サービスの提供主体について確認しようとしている可能性があります。料金ページが多ければ、ランディングページ上で価格に関する情報が十分ではない可能性があります。よくある質問へ進む人が多ければ、ランディングページだけでは解消できない疑問が残っている可能性があります。

2ページ目の閲覧だけで理由を断定することはできません。しかし「なぜこのページが次に見られているのか」を考えることで、コンテンツや内部リンク、CTAなどの改善につなげられます。

3ページ目以降から見えてくること

2ページ目だけでなく、その後の閲覧も重要です。

例えば、あるページは2ページ目としてはそれほど多くなくても、3ページ目以降までを含めると合計では多く閲覧されていることがあります。その場合、ユーザーが複数のページを経由しながらその情報を探している可能性があります。

ランディングページとの関連が強いページであれば、以下のような改善を検討できます。

検討できる改善案
  • ランディングページから直接リンクした方がよい
  • ページ内でその情報の存在を知らせた方がよい
  • 場合によっては、その情報自体をランディングページに追加した方がよい

反対に、コンバージョンに至るまでに必要以上に多くのページが閲覧されているのであれば、必要な情報を探し回らせている可能性もあります。

ページビュー数が多いことや何ページも回遊していることが、必ずしも良い状態とは限りません。ユーザーが目的を達成するために必要な情報に適切にアクセスできているか、その視点で後続閲覧を把握することがサイト改善には重要です。単純に「回遊率を高める」発想とは異なります。

いちしま泰樹

2ページ目としてはあまり閲覧されていないが、それ以降で多く閲覧されているページがあれば、ランディングページとの関連や改善の可能性を探った方がよい、ということです

改善に必要なのは「完全な経路」なのか

今回の方法は、個々の閲覧経路を完全に復元するものではありません。往々にしてWeb解析ではユーザーの行動を細かく正確に把握しがちです。

もちろん計測の精度は重要です。しかし、分析の目的がWebサイトの改善であるなら「どこまで詳しくユーザー行動を再現できるか」より「改善の判断に必要な情報を揃えているか」の視点も重要です。

今回のケースでは、以下の要素を把握できればコンテンツやリンク、CTA、サイト内の情報設計を見直す材料になります。

把握したい要素
  • ランディングページの次に何が見られているか
  • その後、どのページが繰り返し現れるか
  • 想定している次ページと実際の閲覧にずれがないか
  • ユーザーが数ページを経由して探している情報はないか

各ユーザーの多様な経路が網羅的にわからなくても改善仮説は立てられます。むしろ「完全な経路を取得すること」を目的化するのではなく、「何を判断するためにどの程度のデータが必要なのか」を先に考える方がよいケースは多くあります。

GA4の標準機能だけでは扱いにくいデータであっても、目的に合わせて少し情報を補うことで新しい分析の視点を作ることができます。

今回の「ランディングページ起点の閲覧順別ページ分布」もその一つです。ランディングページをそのページ単体で評価するのではなく、そのページを起点とするユーザーセッションがその後Webサイトでどのような情報を閲覧しているのかまで含めて評価する。そうすることで、ランディングページだけでなくページ同士の関係やサイト内の情報設計についても具体的な改善を考えることができます。

付録:GTMの実装コード

この記事で紹介した計測を行うためのカスタムHTMLタグのコードです。

カスタムHTMLタグ サンプルコード
<script>
(function () {
  // ==================================================
  // 設定:導入サイトに合わせて変更する主な箇所
  // ==================================================

  // セッション内ページ閲覧順を保持するCookie名
  var COOKIE_NAME = 'ga_spvi';

  // セッションのランディングページを保持するCookie名
  // Cookie名は短さを優先し、entry のままにしています。
  var COOKIE_LANDING_NAME = 'ga_spvi_entry';

  // Cookieを共有するドメイン
  // 例:www.example.com、blog.example.com などのサブドメイン間で共有したい場合は 'example.com' を指定します。
  // 本番導入時は、導入サイトに合わせて必ず確認してください。
  var COOKIE_DOMAIN = 'example.com';

  // CookieのPath
  var COOKIE_PATH = '/';

  // セッションタイムアウト設定:30分
  // GA4側でセッションタイムアウトを変更している場合は、その時間に合わせて変更してください。
  var SESSION_TIMEOUT_MINUTES = 30;

  // GA4へ送信するイベントパラメータ名:セッション内ページ閲覧順
  var DATA_LAYER_INDEX_KEY = 'session_page_view_index';

  // GA4へ送信するイベントパラメータ名:セッションのランディングページ
  var DATA_LAYER_LANDING_KEY = 'session_landing_page_location';

  // セッションのランディングページとして保存・送信する文字列の最大長
  // location.hostname + location.pathname がこの文字数を超える場合は切り捨てます。
  var LANDING_PAGE_MAX_LENGTH = 100;

  // CookieのSameSite属性
  var SAME_SITE = 'Lax';

  // HTTPSページではSecure属性を付与
  var USE_SECURE_COOKIE = (location.protocol === 'https:');

  // GTMの組み込み変数「Container ID」を有効化しておく
  var CONTAINER_ID = '{{Container ID}}';

  // デバッグログを出す場合は true
  var DEBUG = false;

  // ==================================================
  // ここから下は通常変更不要
  // ==================================================

  var SESSION_TIMEOUT_MS = SESSION_TIMEOUT_MINUTES * 60 * 1000;
  var COOKIE_MAX_AGE_SECONDS = SESSION_TIMEOUT_MINUTES * 60;

  function log(message) {
    if (DEBUG && window.console && typeof window.console.log === 'function') {
      window.console.log('[session_page_view_index] ' + message);
    }
  }

  function normalizeCookieDomain(domain) {
    if (!domain) {
      return '';
    }
    return String(domain).replace(/^\./, '').toLowerCase();
  }

  function getCookie(name) {
    var encodedName = encodeURIComponent(name) + '=';
    var cookies = document.cookie ? document.cookie.split(';') : [];

    for (var i = 0; i < cookies.length; i++) {
      var cookie = cookies[i].replace(/^\s+/, '');

      if (cookie.indexOf(encodedName) === 0) {
        return decodeURIComponent(cookie.substring(encodedName.length));
      }
    }

    return '';
  }

  function setCookie(name, value, maxAgeSeconds) {
    var cookieText = encodeURIComponent(name) + '=' + encodeURIComponent(value);

    cookieText += '; Max-Age=' + String(maxAgeSeconds);
    cookieText += '; Path=' + COOKIE_PATH;

    var domain = normalizeCookieDomain(COOKIE_DOMAIN);
    if (domain) {
      cookieText += '; Domain=' + domain;
    }

    if (SAME_SITE) {
      cookieText += '; SameSite=' + SAME_SITE;
    }

    if (USE_SECURE_COOKIE) {
      cookieText += '; Secure';
    }

    document.cookie = cookieText;
  }

  function parseState(value) {
    var state = {
      index: 0,
      lastTime: 0,
      isValid: false
    };

    if (!value) {
      return state;
    }

    var parts = value.split('|');

    if (parts.length < 2) {
      return state;
    }

    var index = Number(parts[0]);
    var lastTime = Number(parts[1]);

    if (
      !isNaN(index) &&
      index > 0 &&
      !isNaN(lastTime) &&
      lastTime > 0
    ) {
      state.index = Math.floor(index);
      state.lastTime = lastTime;
      state.isValid = true;
    }

    return state;
  }

  function truncateValue(value, maxLength) {
    var text = String(value || '');

    if (maxLength > 0 && text.length > maxLength) {
      return text.substring(0, maxLength);
    }

    return text;
  }

  function getCurrentLandingPage() {
    // プロトコル、クエリパラメータ、ハッシュは含めない
    // 例:www.example.com/example/0001
    var host = String(location.hostname || '').toLowerCase();
    var path = String(location.pathname || '/');

    return truncateValue(host + path, LANDING_PAGE_MAX_LENGTH);
  }

  function setDataLayerValues(values) {
    var gtm = window.google_tag_manager && window.google_tag_manager[CONTAINER_ID];

    if (!gtm || !gtm.dataLayer || typeof gtm.dataLayer.set !== 'function') {
      return false;
    }

    for (var key in values) {
      if (Object.prototype.hasOwnProperty.call(values, key)) {
        gtm.dataLayer.set(key, values[key]);
      }
    }

    return true;
  }

  try {
    var now = Date.now();

    var currentValue = getCookie(COOKIE_NAME);
    var currentLandingValue = getCookie(COOKIE_LANDING_NAME);

    var state = parseState(currentValue);

    var currentIndex = state.index;
    var lastTime = state.lastTime;
    var landingPage = currentLandingValue;

    // 初回、Cookieなし、Cookie値不正、セッションタイムアウト超過、端末時刻の巻き戻り、
    // またはランディングページCookieがない場合は新しいセッション扱い
    var isNewSession = (
      !state.isValid ||
      !lastTime ||
      now - lastTime > SESSION_TIMEOUT_MS ||
      now < lastTime ||
      !landingPage
    );

    if (isNewSession) {
      currentIndex = 0;
      landingPage = getCurrentLandingPage();
    } else {
      landingPage = truncateValue(landingPage, LANDING_PAGE_MAX_LENGTH);
    }

    var nextIndex = currentIndex + 1;
    var nextValue = String(nextIndex) + '|' + String(now);

    // Cookieを書き込み
    setCookie(COOKIE_NAME, nextValue, COOKIE_MAX_AGE_SECONDS);
    setCookie(COOKIE_LANDING_NAME, landingPage, COOKIE_MAX_AGE_SECONDS);

    // Cookieを書き込めた場合のみ、値をGTMのデータレイヤーへ渡す
    if (getCookie(COOKIE_NAME) !== nextValue) {
      log('Index cookie could not be written or read. Parameters were not set.');
      return;
    }

    if (getCookie(COOKIE_LANDING_NAME) !== landingPage) {
      log('Landing page cookie could not be written or read. Parameters were not set.');
      return;
    }

    // タグ順序付け方式では dataLayer.push() ではなく dataLayer.set() で値を渡す
    var values = {};
    values[DATA_LAYER_INDEX_KEY] = nextIndex;
    values[DATA_LAYER_LANDING_KEY] = landingPage;

    if (setDataLayerValues(values)) {
      log(DATA_LAYER_INDEX_KEY + ' = ' + nextIndex);
      log(DATA_LAYER_LANDING_KEY + ' = ' + landingPage);
    } else {
      log('GTM dataLayer.set was not available. Parameters were not set.');
    }
  } catch (e) {
    // Cookieが利用できない環境や想定外のエラーが発生した場合でも、通常のGA4計測を妨げない
    log('Error occurred. Parameters were not set.');
  }
})();
</script>

以下は実装設定時の注意点です。簡略化した案内に留めます。

Googleタグマネージャーでの実装設定時の注意点
  • カスタムHTMLタグのコードでは、CookieのドメインやGA4のセッションタイムアウトなどを導入サイトに合わせて設定する必要があります。Cookie同意やConsent Modeを運用しているサイトでは各自のCookie利用方針や説明文への記載要否を確認してください。
  • Cookieドメインの設定により、同一親ドメイン配下のサブドメイン間では値を共有できます。異なるドメイン間ではCookieを共有できません。
  • 「session_landing_page_location」の文字数はデフォルトで最大100文字に設定にしています。無償版のGA4ではイベントパラメータ値の上限が100文字のためです。
  • GA4の page_view イベントにイベントパラメータ2件の追加、およびカスタムディメンション2件の設定が必要です。
  • カスタムHTMLタグの発火タイミングは、既存のGoogleタグまたは page_view を送るGA4イベントタグ側の「タグの順序付け>このタグが発火する前にタグを配信」で制御します。カスタムHTMLタグに初期化トリガーなどを直接設定すると、二重発火の可能性があります。
  • 仮想ページビューを計測している場合は、仮想の page_view を送信しているイベントタグにも同様にタグ順序付けを設定する必要があります。通常のページ読み込みを前提としているため、SPAや仮想ページビューでは個別の設定と検証が必要です。
  • GTMのプレビューとGA4のDebugViewで、1ページ目に閲覧順「1」とランディングページが付与され、2ページ目以降で閲覧順だけが増えていること、page_view が二重送信されていないことを確認してください。

導入や既存のGTM環境への組み込み、動作確認などの支援が必要な場合は、お問い合わせください。

GA計測設定の見直しの支援をしています。

記事をシェアする

株式会社真摯 代表取締役。マーケティング視点と分析データの根拠を元に、Webサイトの分析改善やKPI設計など企業のデジタル領域のビジネス改善を支援している。

大学卒業後、外食チェーンストアに入社。その後の百貨店での勤務も含め、店舗現場での実務や接客コミュニケーションが仕事の原点。2002年にWebビジネスの世界に入り、2004年からアクセス解析を軸としたWebマーケティング支援を手掛ける。Web制作会社とインターネット広告代理店を経て2010年に独立、のち法人化、現在に至る。

プロフィール詳細

真摯のニュースレターを購読

月1回ほどコラムや情報をお届けいたします。いつでも購読解除できます。サンプル

会社案内の資料をダウンロードしていただけます

2営業日以内にご連絡差し上げます

目次