Web SDK - Google タグマネージャ実装ガイド

ドキュメント

概要

インフォメーションWeb Attributionは企業向け機能です。お客様のアカウントでこの機能を有効にするには、カスタマーサクセスマネージャーにお問い合わせください。

このガイドでは、Google Tag Manager(GTM)を使用してSingular Web SDKを実装する方法を説明します。この方法は、ウェブサイトのコードに直接アクセスできないチームや、GTMでトラッキングを管理したいチームに最適です。

重要同じサイトでGTMとネイティブJavaScriptの両方を使用しないでください。重複トラッキングやイベント数の膨張を防ぐため、1つの方法のみを選択してください。Singularはイベントを自動的に重複排除しません。

  • Native JavaScriptとGoogle Tag Managerの両方のメソッドを実装しないでください。重複トラッキングを避けるために1つだけを選択してください。
  • Singular Web SDKはユーザーのブラウザでクライアントサイドで動作するように設計されています。正しく機能するためにはlocalStorageや DOM(Document Object Model)などのブラウザ機能にアクセスする必要があります。SDKをサーバーサイドで実行しようとしないでください(Next.js SSRやnode.jsなど)。サーバー環境ではブラウザAPIにアクセスできないため、トラッキングに失敗します。

前提条件

開始する前に、以下を確認してください:

実装ステップ


ステップ 1: Singular Web Tracking テンプレートを GTM コンテナに追加します。

  1. Google Tag Managerアカウントにログインし、ウェブサイトのコンテナを選択します。
  2. タグ>新規作成に進みます。
  3. タグに名前を付けます:"Singular Init Tag"
  4. タグ設定ボックスをクリックしてタグの設定を開始します。
  5. タグタイプ: を選択し、「コミュニティテンプレートギャラリーで他のタグタイプを見る」を選択します。
  6. Singular "を検索し、"Singular Web Tracking "を選択します。Add to Workspace "ボタンをクリックします。

ステップ2:SDKの初期化

  1. フォームフィールドに以下を入力します:
    • トラックタイプを初期化に設定します。
    • Apiキーフィールドに実際のSDKキーを入力します
    • Secretフィールドに実際のSDK Secretを入力します
    • 実際のプロダクトIDを入力します。com.website-name SingularプラットフォームのAppsページにあるBundleIDの値と一致する必要があります。

      TIP! com.website-name.dev のテスト用に特定のプロダクトIDを使用し、本番環境にプッシュする前に更新してください。こうすることで、Singularのレポーティングでテストデータを本番アプリから切り離すことができます。

    • オプションです:
      • ログレベル: コンソールへのSDKデバッグロギングの設定。デフォルトはnoneです。
      • セッションタイムアウト: SDKが新しいセッションを作成するまでの、ユーザーの非アクティブ時間。Singularは、ユーザーのリテンションを計算し、再エンゲージメントのアトリビューションを有効にするために、ユーザーセッションを送信します。デフォルト値は30分です。
      • クロスサブドメイントラッキング
  2. このタグを動作させるためのトリガーを設定するには、トリガーを選択します。
  3. Newを選び、Tiggerに名前をつける:「Singular Init Trigger "と名付ける。
  4. Trigger Configurationをクリックし、"Page View - Window Loaded"を選択し、"Save"をクリックする。
  5. もう一度"Save"をクリックし、タグを保存する。
  6. Tagページから"Preview"をクリックし、Singular Initialization Tagがトリガーされることをテストする。

    singular_init_tag_fired.png

成功!プレビューコンソールの[Tags Fired]セクションに"Singular Init Tag"が表示されれば、初期化タグの設定は成功です。

重要! SPA(シングルページアプリケーション)では、別のページにルーティングするたびにPage VisittrackType をトリガーしてください。初期化はすでにページ訪問を報告しているため、最初に読み込まれたページではページ訪問を呼び出さないでください。

解決策の概要

  • カスタムJavaScript変数を使用して、最初のページロードかどうかを検出する。
  • 2つのタグを設定する:
    • "Singular Init Tag" (Track Type = Initialization)を設定し、最初のページロード時にのみ発火するようにする。
    • "Singular Page Visit Tag" (Track Type = Page Visit)は、History Changeトリガーを使って、ルート変更(最初のロードを除く)ごとに発火します。
  • SPAがルート変更の履歴イベントをdataLayerにプッシュするようにします。

image5.png


ステップ 3: イベントのトラッキング

SDKを初期化した後、ユーザーがウェブサイト上で重要なアクションを実行したときにカスタムイベントをトラッキングできます。

重要Singularは重複イベントをブロックしません!ページの更新や重複に対する保護を追加するのは開発者の責任です。誤った収益データを防ぐために、収益イベント専用の重複排除方法を組み込むことをお勧めします。例は下記の「ステップ5:重複イベントの防止」をご覧ください。

Basic EventConversion EventRevenue Event

基本的なイベントトラッキング

シンプルなイベントをトラッキングするか、有効なJSONを使用してカスタム属性を追加し、イベントに関するより多くのコンテキストを提供します:

  1. Singular Web Trackingテンプレートを使って、カスタムイベント用の新しいタグを作成します。

  2. イベントタグに名前を付けます。

  3. トラックタイプ = カスタムイベントを選択します。

  4. Event Name "フィールドを調整するか、適切な変数を設定します。

  5. Custom User Id "フィールドを調整するか、適切な変数を設定します。

  6. イベントのKey/Valueペアを渡したい場合は、"Attributes "を調整します。

  7. 予想される場合にのみタグを起動するように、適切なトリガーを設定する。

  8. トリガーとタグを保存し、プレビューでテストします。

custom_event.png

一般的なイベント実装パターン

Page Load EventsButton Click EventsForm Submission Events

ページ読み込みイベント用のGTMトリガーの作成

Singular Web SDK を Google Tag Manager で実装するには、ページロード時に発火するページロードトリガーを作成する必要があります。

クイックセットアップGTM でTriggers New Trigger Configurationに移動し、トリガータイプとして"Page View"を選択します。ほとんどの実装では、「すべてのページビュー」を選択して、すべてのページ読み込み時にトリガーを発火させます。

完全なセットアップ手順Googleタグマネージャの公式ドキュメントを参照してください


ステップ4: カスタマーユーザーIDの設定

Singular SDKのメソッドを使用して、内部ユーザーIDをSingularに送信することができます。

注意 :Singularのクロスデバイスソリューションを使用する場合は、すべてのプラットフォームでユーザーIDを収集する必要があります。

注:複数のユーザーが1つのデバイスを使用する場合は、ログインとログアウトごとにユーザーIDを設定および解除するログアウトフローを実装することをお勧めします。

ヒントモバイルSDKで使用しているのと同じカスタマーユーザIDを使用してください。これにより、クロスデバイスのアトリビューションが可能になり、プラットフォーム間でのユーザー行動の完全なビューが提供されます。

ログインしていない状態でユーザーがウェブサイト上でアクションを実行する限り、イベントはSingularが生成したユーザーIDでSingularに送信されます。しかし、ユーザーが登録またはログインした後は、あなたのウェブサイトで使用されているユーザーID、例えばハッシュ化されたメールアドレスと共にイベントをSingularに送信させることができます。

Singularは、ユーザーレベルのデータエクスポート(属性ログのエクスポートを参照)および内部BIポストバックを設定している場合(内部BIポストバックの設定を参照)、ユーザーIDを使用します。

ユーザー ID を Singular に送信する方法は 2 つあります:

  • 推奨: 推奨:ウェブサイトを開いたときのユーザーIDがわかっている場合は、SDKを初期化するときに初期化トラックタイプにユーザーIDを設定します。これにより、最初のページ訪問からユーザーIDをSingularが利用できるようになります。
  • あるいは、Track Type =LoginのTagを任意の時点(通常は認証が発生した後)でトリガーすることもできます。ユーザーIDが利用可能になったらすぐに呼び出すことをお勧めします。注意:このタグを呼び出してもイベントは発生しません。ユーザーIDが設定され、今後のイベントトリガーに追加されるだけです!

SingularでユーザーIDを設定するには、"ログイン "トラックタイプのSingularタグを追加します:

  1. Googleタグマネージャーアカウントで、タグ > 新規をクリックします。
  2. タグ設定ウィンドウで、タグ設定をクリックし、タグタイプメニューで「Singular Web Tracking」を選択します。
  3. トラックタイプで「ログイン」を選択します。
  4. カスタムユーザーID]の下に、ユーザーIDを含むGoogleタグマネージャ変数を入力します。
  5. トリガーをクリックし、トリガーイベントを追加します。
  6. 保存をクリックします。

image4.png

ユーザーIDの設定を解除するには、「ログアウト」トラックタイプのタグを追加します:

  1. Googleタグマネージャーアカウントで、タグ > 新規をクリックします。
  2. タグ設定]ウィンドウで、[タグ設定]をクリックし、[タグタイプ]メニューで、[Singular Web Tracking]を選択します。
  3. トラックタイプで「ログアウト」を選択します。
  4. トリガー」をクリックし、トリガーイベント「ユーザーログアウト」を追加します。
  5. 保存]をクリックします。

image1.png

注意事項

  • ユーザーIDは、ログアウトトラックタイプを使用して設定を解除するか、ユーザーがローカルストレージを削除するまで保持されます
  • ウェブサイトを閉じたり更新したりしても、ユーザーIDは解除されません。
  • シークレットモードなどのプライベートモードでブラウズすると、ブラウザを閉じたときにローカルストレージが自動的に削除されるため、SingularがユーザーIDを保持することはありません。

ステップ 5: イベントの重複を防ぐ

Googleタグマネージャで重複イベントを防ぐ

重要です!これは最も一般的なGTMの実装ミスの一つです。適切な保護策を講じないと、ユーザーがページを更新したり、ナビゲーションを戻したり、アクションを再トリガーしたときに、Singular Web SDKのイベントが複数回発生し、メトリックスを著しく悪化させる可能性があります。

GTMで重複が発生する理由

Google Tag Managerはページロードごとにトリガーを評価するため、ユーザーがサンキューページを更新したり、フォームを再送信したり、ブラウザの戻る/進むボタンで移動したりすると、タグが再送信されます。これは、GTM にカスタムイベント用のセッション認識が組み込まれていないために起こります。


GTM の重複排除方法

セッションストレージのアプローチ:ブラウザの sessionStorage に一意のイベント識別子を保存し、ユーザセッション中にどのイベントが既に発生したかを追跡する。

カスタム JavaScript 変数:新しいイベントのトリガーを許可する前に、localStorage に過去に発生したイベントがないかチェックする変数を作成する。

トリガー条件:トリガーに条件ロジックを追加し、インジケーターが重複している場合にイベントが発生しないようにします。


実装ステップ

ストレージ変数の作成イベントパラメータとユーザーセッションに基づいて、各Singularイベントに一意の識別子を生成するカスタムJavaScript変数を構築します。

チェック変数を作成します:現在のイベント識別子がすでにブラウザストレージに存在するかどうかをチェックする別のカスタムJavaScript変数を作成します。

トリガー条件を追加します:Singular Web SDKトリガーを修正して、「重複チェック」変数が「false」に等しいという条件を含めます。

起動後の保存:GTMのタグシーケンス機能を使って、Singularイベントが正常に発火した後に、イベント識別子をsessionStorageに保存するカスタムHTMLタグを発火させます。


GTM 固有の考慮事項

タグシーケンス:Advanced Settings Tag Sequencingを使用して、メインのSingularタグが完了した後にStorageタグが発火するようにします。

変数の評価:カスタム JavaScript 変数は参照されるたびに評価されるので、パフォーマンスのために最適化してください。

クロスドメインの制限:SessionStorageはドメイン固有なので、必要であれば、クロスドメイントラッキングのためにCookieベースのソリューションを実装してください。

詳細な実装:包括的な GTM 重複排除ガイドを参照してください:


ステップ6: GTMの実装をテストする

  1. GTMプレビューモードを開き、ウェブサイトを読み込みます。
  2. 以下を確認します:
  • SDKがロードされる(singular-sdk.js へのネットワークリクエスト)
  • イベントが期待通りにトリガーされ、アクションごとに1回のみ発生する。
  • ブラウザコンソールでsingularに関連するエラーが発生しない
  • ネットワークリクエストがsdk-api-v1.singular.net
  1. ブラウザ開発者ツールの「ネットワーク」タブを使用して、適切なペイロードが送信されていることを確認します。

成功!ネットワークリクエストに正しいSingularイベントが表示され、重複がなければ、本番稼働の準備は完了です!


ステップ7:Web-to-Appフォワーディングの実装

ウェブからアプリへのアトリビューション転送

Singular Web SDKを使用して、ウェブサイトからモバイルアプリへのユーザージャーニーを追跡し、モバイルアプリのインストールや再エンゲージメントへの正確なウェブキャンペーンのアトリビューションを可能にします。 以下の手順に従って、デスクトップユーザー向けのQRコードサポートを含む、ウェブからアプリへの転送を設定してください。

  1. ウェブサイトからモバイルアプリへのアトリビューション転送ガイドに従って、モバイルウェブのアトリビューション用にSingular Web SDKを設定してください。
  2. デスクトップウェブからアプリへのトラッキング
    • QRコードジェネレータクリーンアップタグの作成
      1. GTMのタグ > 新規作成に移動し、カスタムHTMLを選択します。
      2. コードを追加します:
        • 選択したQRCodeライブラリをインジェクトします。
        • window.singularSdk.buildWebToAppLink(baselink); からWeb-to-Appリンクを取得します。
        • 返されたリンクからQRCodeを生成します。
        • ページのQRCodeイメージを更新します。
      3. タグに名前を付けます:タグ名:"Singular - QR Code Generator (Cleanup)"
      4. トリガーは必要ありません:Cleanupタグはメインタグからトリガーを継承します。
      5. Singular Initタグを設定します:
        • タグ設定をクリックします。
        • Advanced Settings(詳細設定)> Tag Sequencing(タグシーケンス)に進みます。
        • Singular Initタグ]が起動した後にクリーンアップタグを起動する]をチェックします。
        • ドロップダウンからQRコードジェネレータタグを選択します。
        • タグを保存し、プレビューモードでテストします。

ヒントモバイルのアプリ内ブラウザのウェブビュー(Facebook、Instagram、TikTokで使用されているような)は、ユーザーがデバイスのネイティブブラウザに移動するとSingularデバイスIDが変更され、アトリビューションが中断される可能性があります。

これを防ぐには、各広告ネットワークで常に適切な Singular トラッキングリンク形式を使用してください:


高度なトピック

グローバルプロパティの追加

グローバルプロパティ
#

Singular SDKでは、アプリから送信されるすべてのセッションやイベントと一緒にSingularサーバーに送信するカスタムプロパティを定義することができます。これらのプロパティは、ユーザーやアプリのモード/ステータスなど、あらゆる情報を表すことができます。

  • 有効なJSONオブジェクトとして、最大5つのグローバルプロパティを定義できます。グローバルプロパティは、クリアされるかブラウザのコンテキストが変更されるまで、ブラウザlocalstorage に保持されます。

  • 各プロパティ名と値の長さは200文字までです。これより長いプロパティ名や値を渡すと、200文字に切り詰められます。

  • グローバルプロパティは現在Singularのユーザーレベルのイベントログ(属性ログのエクスポートを参照)とポストバックに反映されます。

  • グローバルプロパティは、Singularからサードパーティへのポストバックで送信され、マッチング目的で使用することができます。

SDK初期化前にグローバルプロパティを設定することは、現在Google Tag Manager初期化タグではサポートされていません。 しかし、初期化後にグローバルプロパティを処理するには、カスタムHTMLタグを作成し、ネイティブJavaScript関数を実行する必要があります:setGlobalProperties() getGlobalProperties(),clearGlobalProperties().

グローバルプロパティを処理するためのカスタムHTMLタグの作成

  1. Googleタグマネージャのインターフェイスで、左のナビゲーションメニューから「タグ」をクリックし、「新規」ボタンをクリックして新しいタグを作成します。

  2. タグ設定」をクリックし、利用可能なタグタイプのリストから「カスタムHTML」を選択します。

  3. HTMLフィールドに、グローバルプロパティメソッドコードを貼り付けます。 以下のオプションを参照してください:

  4. Triggering(トリガー)」をクリックし、タグにトリガー・オプションを割り当てます。

  5. 上部のタグ名フィールドで、タグに「Singular - Set Global Properties」のような説明的な名前を付けます。

グローバル・プロパティを設定すると、設定が解除されるかクリアされるまで、すべてのイベントでグローバル・プロパティが保持されます。

プレビューをクリックして、実装をテストしてください。ブラウザコンソールで、グローバルプロパティが設定されていることを確認します。

詳細設定について:Googleの公式ドキュメント「カスタムHTML」を参照してください。

Setting Global PropertiesGetting GlobalPropertiesClearing Global Properties

グローバルプロパティ設定用カスタムHTMLタグコード

<script>
/**
 * Set a Singular global property before SDK initialization.
 * Allows up to 5 key/value pairs. Optionally overwrites existing value for a key.
 * 
 * @param {string} propertyKey - The property key to set.
 * @param {string} propertyValue - The property value to set.
 * @param {boolean} overrideExisting - Whether to overwrite the property if it already exists.
 */

// Example usage - customize these values for your implementation
window.singularSdk.setGlobalProperties('user_type', 'premium', true);
window.singularSdk.setGlobalProperties('app_version', '2.1.0', false);
</script>

オーガニック検索のトラッキング

オーガニック検索のトラッキング例
#

オーガニック・サーチ・トラッキング設定タグの作成

重要!- このタグはSingular SDKの初期化の前に実行する必要があります

このカスタムHTMLタグはドキュメントのURLを変更し、Singular Web SDKが初期化中に読み取る必要のあるオーガニック検索トラッキングパラメータ(wpsrcとwpcn)を追加します。適切な実行順序を確保するために、タグの順序は重要です。

この例はオーガニック検索トラッキングを有効にするための回避策として提供されています。このコードはあくまでも例として使用し、マーケティング部門のニーズに基づいてウェブ開発者が更新、管理する必要があります。 Organic Searchトラッキングは広告主によって意味が異なる場合があります。 サンプルを確認して、ニーズに合わせて調整してください。

これを使用する理由

  • キャンペーンパラメータが存在しない場合でも、オーガニック検索の訪問が適切にトラッキングされるようにします。

  • Singular「Source」パラメータwpsrc(リファラー)の値と「Campaign Name」パラメータwpcn 「OrganicSearch」としてURLに追加し、明確なアトリビューションを実現します。

  • 後で使用できるように、現在のURLとリファラーをlocalStorage

  • 純粋なJavaScriptで、依存性はゼロ。

仕組み

  1. ページのURLにGoogle、Facebook、TikTok、UTMなどのキャンペーンパラメータがあるかチェックします。

  2. キャンペーンパラメータが存在せず、リファラーが検索エンジンの場合、:

    • wpsrc (リファラーを値として)
    • wpcn (OrganicSearchを値として)
  3. ページをリロードすることなく、ブラウザのURLを更新します。

  4. 現在のURLとリファラーをsng_urlsng_ref としてlocalStorageに保存します。

使用方法

  1. GTMの「タグ」>「新規」と進み、「タグ設定」をクリックし、「カスタムHTML」を選択する。
  2. 以下に示すように、完全な JavaScript コードを貼り付けます。
  3. このタグはSingular Init Tagの前に実行されなければならないので、Tag Sequencingを使ってInitの前に実行されるように設定します。

    1. Singular - Organic Search Setup」のようなわかりやすい名前を使います。
    2. Singular Web SDK Init Tagを開きます。
    3. Tag Configuration(タグ設定)をクリックします。
    4. Advanced Settings(詳細設定)> Tag Sequencing(タグシーケンス)に進みます。
    5. Singular Init Tag]が起動する前にセットアップタグを起動する]をチェックします。
    6. ドロップダウンリストから「Singular - Organic Search Setup」タグを選択します。
    7. 推奨セットアップタグ]が失敗した場合、[Singular Init Tag]を起動しない]にチェックを入れ、不完全なURL変更での初期化を防ぎます。
    8. organic_search.png
    9. タグを保存します。
  4. GTMのプレビューモードで確認してください:

    • setupタグが最初に起動し、URLを変更します。
    • Singular Initタグは2番目に起動し、変更されたURLパラメータを読み取ります。
    • タグ間でタイミングの競合が発生しない。

高度なタグシーケンスGoogleの公式ドキュメントを参照してください:


Organic Search Tracking Code
<script>
(function() {
    // singular-web-organic-search-tracking: setupOrganicSearchTracking.js
    // Tracks organic search referrals by appending wpsrc and wpcn to the URL if no campaign parameters exist and the referrer is a search engine.

    // Configuration for debugging (set to true to enable logs)
    var debug = true;

    // List of campaign parameters to check for exclusion
    var campaignParams = [
        'gclid', 'fbclid', 'ttclid', 'msclkid', 'twclid', 'li_fat_id',
        'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'wpsrc'
    ];

    // Whitelist of legitimate search engine domains (prevents false positives)
    var legitimateSearchEngines = new Set([
        // Google domains
        'google.com', 'google.co.uk', 'google.ca', 'google.com.au', 'google.de', 
        'google.fr', 'google.it', 'google.es', 'google.co.jp', 'google.co.kr',
        'google.com.br', 'google.com.mx', 'google.co.in', 'google.ru', 'google.com.sg',
        
        // Bing domains  
        'bing.com', 'bing.co.uk', 'bing.ca', 'bing.com.au', 'bing.de',
        
        // Yahoo domains
        'yahoo.com', 'yahoo.co.uk', 'yahoo.ca', 'yahoo.com.au', 'yahoo.de',
        'yahoo.fr', 'yahoo.it', 'yahoo.es', 'yahoo.co.jp',
        
        // Other search engines
        'baidu.com', 'duckduckgo.com', 'yandex.com', 'yandex.ru',
        'ask.com', 'aol.com', 'ecosia.org', 'startpage.com', 
        'qwant.com', 'seznam.cz', 'naver.com', 'daum.net'
    ]);

    // Extract main domain from hostname (removes subdomains)
    function getMainDomain(hostname) {
        if (!hostname) return '';
        
        var lowerHost = hostname.toLowerCase();
        
        // Handle special cases for known search engines with country codes
        var searchEnginePatterns = {
            'google': function(host) {
                // Match google.TLD patterns more precisely
                if (host.indexOf('google.co.') !== -1 || host.indexOf('google.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'google') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            },
            'bing': function(host) {
                if (host.indexOf('bing.co') !== -1 || host.indexOf('bing.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'bing') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            },
            'yahoo': function(host) {
                if (host.indexOf('yahoo.co') !== -1 || host.indexOf('yahoo.com') !== -1) {
                    var parts = host.split('.');
                    for (var i = 0; i < parts.length - 1; i++) {
                        if (parts[i] === 'yahoo') {
                            return parts.slice(i).join('.');
                        }
                    }
                }
                return null;
            }
        };
        
        // Try specific patterns for major search engines
        for (var engine in searchEnginePatterns) {
            if (lowerHost.indexOf(engine) !== -1) {
                var result = searchEnginePatterns[engine](lowerHost);
                if (result) return result;
            }
        }
        
        // Handle other known engines with simple mapping
        var otherEngines = {
            'baidu.com': 'baidu.com',
            'duckduckgo.com': 'duckduckgo.com', 
            'yandex.ru': 'yandex.ru',
            'yandex.com': 'yandex.com',
            'ask.com': 'ask.com',
            'aol.com': 'aol.com',
            'ecosia.org': 'ecosia.org',
            'startpage.com': 'startpage.com',
            'qwant.com': 'qwant.com',
            'seznam.cz': 'seznam.cz',
            'naver.com': 'naver.com',
            'daum.net': 'daum.net'
        };
        
        for (var domain in otherEngines) {
            if (lowerHost.indexOf(domain) !== -1) {
                return otherEngines[domain];
            }
        }
        
        // Fallback: Extract main domain by taking last 2 parts (for unknown domains)
        var parts = hostname.split('.');
        if (parts.length >= 2) {
            return parts[parts.length - 2] + '.' + parts[parts.length - 1];
        }
        
        return hostname;
    }

    // Get query parameter by name, using URL.searchParams with regex fallback for IE11
    function getParameterByName(name, url) {
        if (!url) url = window.location.href;
        try {
            return new URL(url).searchParams.get(name) || null;
        } catch (e) {
            if (debug) console.warn('URL API not supported, falling back to regex:', e);
            name = name.replace(/[\[\]]/g, '\\$&');
            var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
            var results = regex.exec(url);
            if (!results) return null;
            if (!results[2]) return '';
            return decodeURIComponent(results[2].replace(/\+/g, ' '));
        }
    }

    // Check if any campaign parameters exist in the URL
    function hasAnyParameter(url, params) {
        for (var i = 0; i < params.length; i++) {
            if (getParameterByName(params[i], url) !== null) {
                return true;
            }
        }
        return false;
    }

    // Improved search engine detection - only checks hostname, uses whitelist
    function isSearchEngineReferrer(referrer) {
        if (!referrer) return false;
        
        var hostname = '';
        try {
            hostname = new URL(referrer).hostname.toLowerCase();
        } catch (e) {
            // Fallback regex for hostname extraction (IE11 compatibility)
            var match = referrer.match(/^(?:https?:\/\/)?([^\/\?#]+)/i);
            hostname = match ? match[1].toLowerCase() : '';
        }
        
        if (!hostname) return false;
        
        // First check: exact match against whitelist
        if (legitimateSearchEngines.has(hostname)) {
            if (debug) console.log('Exact match found for:', hostname);
            return true;
        }
        
        // Second check: subdomain of legitimate search engine
        var hostParts = hostname.split('.');
        if (hostParts.length >= 3) {
            // Try domain.tld combination (e.g., google.com from www.google.com)
            var mainDomain = hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
            if (legitimateSearchEngines.has(mainDomain)) {
                if (debug) console.log('Subdomain match found for:', hostname, '-> main domain:', mainDomain);
                return true;
            }
            
            // Try last 3 parts for country codes (e.g., google.co.uk from www.google.co.uk)
            if (hostParts.length >= 3) {
                var ccDomain = hostParts[hostParts.length - 3] + '.' + hostParts[hostParts.length - 2] + '.' + hostParts[hostParts.length - 1];
                if (legitimateSearchEngines.has(ccDomain)) {
                    if (debug) console.log('Country code domain match found for:', hostname, '-> cc domain:', ccDomain);
                    return true;
                }
            }
        }
        
        if (debug) {
            console.log('Hostname not recognized as legitimate search engine:', hostname);
        }
        
        return false;
    }

    // Main function to update URL with organic search tracking parameters
    function setupOrganicSearchTracking() {
        var url = window.location.href;
        var referrer = document.referrer || '';

        // Store URL and referrer in localStorage
        try {
            localStorage.setItem('sng_url', url);
            localStorage.setItem('sng_ref', referrer);
        } catch (e) {
            if (debug) console.warn('localStorage not available:', e);
        }

        if (debug) {
            console.log('Current URL:', url);
            console.log('Referrer:', referrer);
        }

        // Skip if campaign parameters exist or referrer is not a search engine
        var hasCampaignParams = hasAnyParameter(url, campaignParams);
        if (hasCampaignParams || !isSearchEngineReferrer(referrer)) {
            if (debug) console.log('Skipping URL update: Campaign params exist or referrer is not a legitimate search engine');
            return;
        }

        // Extract and validate referrer hostname
        var referrerHostname = '';
        try {
            referrerHostname = new URL(referrer).hostname;
        } catch (e) {
            if (debug) console.warn('Invalid referrer URL, falling back to regex:', e);
            var match = referrer.match(/^(?:https?:\/\/)?([^\/]+)/i);
            referrerHostname = match ? match[1] : '';
        }

        // Extract main domain from hostname
        var mainDomain = getMainDomain(referrerHostname);
        
        if (debug) {
            console.log('Full hostname:', referrerHostname);
            console.log('Main domain:', mainDomain);
        }

        // Only proceed if main domain is valid and contains safe characters
        if (!mainDomain || !/^[a-zA-Z0-9.-]+$/.test(mainDomain)) {
            if (debug) console.log('Skipping URL update: Invalid or unsafe main domain');
            return;
        }

        // Update URL with wpsrc and wpcn parameters
        var urlObj;
        try {
            urlObj = new URL(url);
        } catch (e) {
            if (debug) console.warn('URL API not supported, cannot modify URL:', e);
            return;
        }
        
        // Set wpsrc to the main domain (e.g., google.com instead of tagassistant.google.com)
        urlObj.searchParams.set('wpsrc', mainDomain);
        
        // Set wpcn to 'Organic Search' to identify the campaign type
        urlObj.searchParams.set('wpcn', 'Organic Search');

        // Update the URL without reloading (check if history API is available)
        if (window.history && window.history.replaceState) {
            try {
                window.history.replaceState({}, '', urlObj.toString());
                if (debug) console.log('Updated URL with organic search tracking:', urlObj.toString());
            } catch (e) {
                if (debug) console.warn('Failed to update URL:', e);
            }
        } else {
            if (debug) console.warn('History API not supported, cannot update URL');
        }
    }

    // Execute the function
    setupOrganicSearchTracking();
})();
</script>

クロスサブドメイントラッキング

デフォルトでは、Singular Website SDKはSingular Device IDを生成し、ブラウザのストレージを使用して永続化します。このストレージはサブドメイン間で共有できないため、SDKはサブドメインごとに新しいIDを生成することになります。

サブドメイン間でSingular Device IDを永続化したい場合は、以下のいずれかの方法をご利用ください:

方法B(詳細):Singular Device IDを手動で設定する
#

方法B(詳細):シングルデバイスIDを手動で設定する

Singular SDKにデバイスIDを自動的に保持させたくない場合は、例えばトップレベルドメインCookieやサーバーサイドCookieを使って、ドメイン間でIDを手動で保持することができます。この値は、有効なuuid4形式でSingularが以前に生成したIDでなければなりません。

注:カスタムJavaScript変数を定義し、Init track-typeタグを呼び出した後にsingularSdk.getSingularDeviceId()を呼び出すことで、SingularデバイスIDを読み取ることができます。

mceclip2.png


一般的なGTM実装の問題

必要なデバイス識別子
イベントが複数回発生する トリガーを絞り込むか制限するか、「ページごとに1回」を使用するか、条件を追加してください。
製品IDの形式が正しくない プロダクトIDは逆DNS記法(com.company.site )を使用する必要があります。
イベントがトラッキングされない イベント名のスペルや大文字小文字をチェックする。singularSDKがイベントタグが発火する前にロードされていることを確認する。
SDKスクリプトがブロックされている 広告ブロッカーまたは制限的なコンテンツ・セキュリティ・ポリシー。

ベストプラクティス

  • GTMを整理整頓する:タグとトリガーに明確な名前を付け、その目的を文書化する。
  • 未使用のタグや古いタグがないか定期的に監査する。
  • 重複イベントのリスクを減らすために、トリガーの数を最小限にする。
  • 変更後は、本番稼動前に必ずGTMのプレビューモードで十分にテストする。
  • Cookieベースのトラッキング(クロスサブドメイントラッキング)を使用している場合は、プライバシーポリシーを適宜更新する。

関連記事