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

文書

概要

INFO:Web Attributionはエンタープライズ向け機能です。アカウントでこの機能を有効にするには、カスタマーサクセスマネージャーにお問い合わせください。

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

重要!同一サイトでGTMとネイティブJavaScriptの両方を実装しないでください。重複トラッキングやイベントカウントの過大計上を防ぐため、いずれか一方の方法のみを選択してください。Singularはイベントの重複排除を自動で行いません。

  • ネイティブJavaScriptとGoogle Tag Managerの両方の方法を実装しないでください。重複トラッキングを避けるため、いずれか一方のみを選択してください。
  • Singular Web SDKはユーザーのブラウザ内でクライアントサイドに動作するよう設計されています。正常に機能するには、localStorageや Document Object Model(DOM)などのブラウザ機能へのアクセスが必要です。サーバーサイド(例:Next.js SSRやnode.js経由)でSDKを実行しようとしないでください。サーバー環境ではブラウザAPIへのアクセスが提供されないため、トラッキングが失敗します。

前提条件

開始前に、以下が用意されていることを確認してください:

実装手順


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

  1. Google タグマネージャーアカウントにログインし、ウェブサイトのコンテナを選択します。
  2. [タグ] > [新規] に移動します。
  3. タグに「Singular Init Tag」という名前を付けます
  4. タグ設定ボックスをクリックしてタグ設定を開始します。
  5. タグの種類を選択: 「コミュニティテンプレートギャラリーでさらにタグの種類を探る」を選択します。
  6. 「Singular」を検索し、「Singular Web Tracking」を選択します。「ワークスペースに追加」ボタンをクリックします。

ステップ2: SDKの初期化

  1. 以下の内容でフォームフィールドを入力してください:
    • トラッキングタイプを「初期化」に設定
    • Api Keyフィールドに実際のSDKキーを入力
    • シークレット欄に実際のSDKシークレットを入力 実際のプロダクトIDを入力
    • 実際のプロダクトIDを入力してください。 例: com.website-name Singularプラットフォームの「Apps」ページにある BundleID 値と一致している必要があります。

      ヒント!テスト用に専用のプロダクトIDを使用してください com.website-name.dev 本番環境へプッシュする前に更新してください。 これにより、Singularレポートにおいてテストデータと本番アプリのデータが分離されます。

    • オプション:
  2. トリガー設定を選択し、このタグを機能させるトリガーを設定します。
  3. 新規」を選択し、トリガー名を「Singular Init Trigger」と命名します
  4. トリガー設定をクリックし、「ページビュー - ウィンドウ読み込み」を選択 「保存」をクリック。
  5. 再度「保存」をクリックしてタグを保存します。
  6. タグページから「プレビュー」をクリックし、 Singular 初期化タグがトリガーされることをテストします。

    singular_init_tag_fired.png

成功!プレビューコンソールの「発火したタグ」セクションに「Singular Init Tag」が表示された場合、初期化タグの設定は正常に完了しています。

重要! SPA(シングルページアプリケーション)の場合 、 異なるページに遷移するたびに ページ訪問トラックタイプをトリガーする必要があります。 最初に読み込まれたページではページ訪問を呼び出さないでください。 初期化タグが既にページ訪問を報告しているためです。

ソリューション概要

  • カスタムJavaScript変数を使用して、最初のページ読み込みかどうかを検出します。
  • 2つのタグを設定します:
    • シングル初期化タグ」(追跡タイプ = 初期化) :最初のページ読み込み時のみ発火
    • シングルページ訪問タグ」(追跡タイプ = ページ 訪問)を、履歴変更トリガーを使用して、 すべてのルート変更時(初回読み込みを除く)に 発火させる。
  • SPAがルート変更時に履歴イベントをdataLayerに確実にプッシュするように設定する。

image5.png


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

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

重要!Singularは重複イベントをブロックしません! ページ再読み込みや重複に対する保護措置は開発者の責任です。誤った収益データを防ぐため、特に収益イベント向けに重複排除手法の導入を推奨します。詳細は下記「ステップ5:重複イベントの防止」の例を参照してください。

Basic EventConversion EventRevenue Event

基本イベント追跡

単純なイベントを追跡するか、有効なJSONを使用してカスタム属性を追加し、 イベントに関する詳細なコンテキストを提供します:

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

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

  3. [Track Type] = [Custom Event] を選択

  4. 「イベント名」フィールドを調整するか、適切な変数を設定します。

  5. 「カスタムユーザーID」フィールドを調整するか、適切な変数を設定します。

  6. 必要に応じて「属性」を調整し、キー/値ペアを イベントに 渡します。

  7. トリガーを設定し、タグが意図したタイミングで発火するようにします。

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

custom_event.png

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

Page Load EventsButton Click EventsForm Submission Events

ページロードイベント用のGTMトリガー作成

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

クイック設定:GTM で トリガー 新規 トリガー設定 に移動し、 トリガータイプとして「ページビュー」を選択します。ほとんどの実装では、 すべてのページロードでトリガーを発動させるため「すべてのページビュー」を選択します。

完全な設定手順については、Google公式のタグマネージャードキュメントを参照してください:


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

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

注: Singularのクロスデバイスソリューションをご利用の場合、 すべてのプラットフォームでユーザーIDを収集する必要があります。 注:複数のユーザーが単一デバイスを使用する場合、 ログイン時とログアウト時にユーザーIDを設定/解除する ログアウトフローの実装を推奨します。

注:複数のユーザーが単一デバイスを使用する場合、ログイン/ログアウト時にユーザーIDを設定 /解除するログアウトフローの実装を推奨します。

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

ユーザーがログインせずにウェブサイト上でアクションを実行している間は、 Singularが生成したユーザーIDでイベントが送信されます。 ただしユーザーが登録またはログイン後は、 ウェブサイトで使用しているユーザーID(例:ハッシュ化されたメールアドレス)を Singularに送信できます。

SingularはユーザーIDを、ユーザーレベルデータエクスポート(アトリビューションログのエクスポート参照)および内部BIポストバック(設定済みの場合、内部BIポストバックの設定参照)で使用します。

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

  • 推奨: ウェブサイト起動時にユーザーIDが判明している場合 、 SDK初期化時に 「Initialization」トラックタイプでユーザーIDを設定します。これにより、 最初のページ訪問時からユーザーIDがSingularに利用可能になります。
  • 代替方法として、認証後など任意のタイミングで 「ログイン」トラックタイプのタグをトリガーできます。 ユーザーIDが利用可能になり次第、すぐに呼び出すことを推奨します。 注:このタグの呼び出しはイベントをトリガーしません。 今後発生するイベントトリガーに追加されるユーザーIDを設定するだけです!

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

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

image4.png

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

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

image1.png

注記: 

  • ユーザーIDは、ログアウト追跡タイプを使用して解除するか、ユーザーがローカルストレージを削除するまで持続します。
  • ウェブサイトの閉じる/更新操作ではユーザーIDは解除されません。
  • シークレットモードなどのプライベートブラウジングでは、ブラウザ終了時にローカルストレージが自動的に削除されるため、SingularがユーザーIDを保持できません。 ステップ5: イベント重複排除(任意)

ステップ5: イベント重複排除(任意)

Google Tag Manager でのイベント重複排除

重要!GTMトリガーが短時間で同一Singularイベントを複数回発火させる場合(例:連続クリックや同一アクションの複数トリガー)、Singularのオプション機能「イベント重複排除」を有効化し、重複エクスポートを自動抑制してください。

GTMで重複が発生する理由

GTMは同一ユーザーアクションに対してタグを複数回評価・発火する可能性があります(例: トリガー条件の重複や複数イベントリスナー)。これにより Singularイベントエクスポートの重複が発生します。


GTM重複排除方法(推奨)

Singular SDKイベント重複排除:Singular Web SDKのGTMテンプレートで重複排除を有効化し、設定可能な時間枠内で同一の重複排除パラメータに一致する重複イベントを削除します。


実装手順

イベント重複排除の有効化:Singular Web SDK 初期化タグ/テンプレートで、 イベント重複排除オプションを有効にします。

eventDeduplication.png

時間枠の設定(オプション):2つのイベントを重複と見なす最大時間枠(ミリ秒単位)を設定します(デフォルト:1000ms / 1秒)。


重複排除の仕組み

有効化時、SDKは以下のパラメータのハッシュ値を用いて、同一イベントが時間枠内で再度発生した場合にイベント(ページ訪問を除く)を抑制します:EventName,EventProductName,IsRevenueEvent,CustomUserId,GlobalProperties,MatchId,WebUrl (さらに収益やカスタム引数などのイベント「追加」ペイロード)。


ステップ6: GTM実装のテスト

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

成功!ネットワークリクエストに正しいSingularイベントが表示され 重複がない場合、本番環境に移行可能です!


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

Web-to-Appアトリビューション転送

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

  1. モバイルウェブアトリビューション用にSingular Web SDKを設定するには、 「ウェブサイトからモバイルアプリへのアトリビューション転送ガイド」 に従ってください。
  2. モバイルWebからアプリへのトラッキング設定:
    • アプリ起動タグを追加し、アプリを開く/インストールするボタンクリックをトリガーに設定します。
      1. Open Appタグの設定時には、 Singularの「リンク管理」ページから モバイルWeb→アプリのベースリンクを指定してください。

        openApp.png

  3. デスクトップWebからアプリへのトラッキングの場合:
    • QRコード生成用クリーンアップタグを作成
      1. GTMで[タグ] > [新規]に移動し、 [カスタムHTML]を選択します。
      2. コードを追加:
        • 選択したQRCodeライブラリをインジェクション
        • Web-to-Appリンクを取得 window.singularSdk.buildWebToAppLink(baselink);
        • 返されたリンクからQRコードを生成。
        • ページ上のQRコード画像を更新します。
      3. タグ名を設定:"Singular - QRコードジェネレーター (クリーンアップ)"
      4. トリガー不要:クリーンアップタグは メインタグからトリガーを継承
      5. Singular Initタグを設定:
        • タグ設定をクリック
        • 詳細設定 > タグシーケンスに移動
        • 「[Singular Init タグ] 発火後にクリーンアップタグを発火」にチェック
        • ドロップダウンからQRコードジェネレータタグを選択
        • タグを保存し、プレビューモードでテストしてください。

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

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


詳細トピック

Singularバナー

シングルバナーを有効にする
#

INFO:シングルバナーはエンタープライズ向け機能です。 この機能の詳細については、カスタマーサクセスマネージャーにお問い合わせください。

シングルバナーはモバイルウェブサイトに表示でき、 ウェブユーザーをシームレスにアプリへ誘導し、 最も関連性の高いアプリコンテンツを表示します。 ウェブサイトでシングルバナーを有効化すると、 組織はシングルバナーのUIを通じてバナーを簡単に設計、 展開、維持できます。

関連記事

ステップバイステップ実装ガイド

  1. ウェブサイトにSingular WebSDK GTM初期化タグを追加してください。

    バナー実装を進める前に、上記の 統合ガイドに従って、ウェブサイトにSingular GTM WebSDKを追加してください。

  2. 初期化タグの設定で、スマートバナーとWeb-to-Appサポートを有効化してください。 高度な設定では、タグ発火優先度を99に設定することを推奨します。 これにより、同じ発火トリガーを持つ他のタグよりも初期化が優先されます。

    gtm-banners.png

  3. バナー表示タグの追加

    ページ読み込み後にスマートバナーを表示するには、 全ページビューでトリガーされるバナー表示タグを追加します。 詳細設定のタグ発火優先度を1に設定することを推奨します。 これにより、このタグの発火が最後に遅延されます。

    showBanner.png

    特定のページでバナーを表示しないようにするには、 「バナー非表示タグ」を追加し、必要に応じてトリガーを設定します。

  4. [詳細オプション] リンク設定のカスタマイズ

    Singularでは、コードを通じて バナー内のリンクをパーソナライズする 方法を提供しています。

    リンクをカスタマイズするには:

    • [バナー表示]タグの設定で、 「リンクパラメータ(オプション)」セクションを展開します。 バナークリック時のリダイレクトロジックに対して、 特定のオーバーライド値を追加できます。

      bannerRedirects.png

    オプション一覧:

    メソッド 説明
    Android Redirect URL Androidアプリのダウンロードページ(通常はPlayストアページ)へのリダイレクトリンクを渡します。 Androidアプリ内のページへのディープリンクを渡します。
    Android Deep Link Androidアプリ内のページへのディープリンクを渡します。
    Android Deferred Deep Link 遅延ディープリンクを渡す。つまり、 ユーザーがまだインストールしていない Androidアプリ内のページへのリンク。
    iOS Redirect URL iOSアプリのダウンロードページ(通常はApp Storeページ)へのリダイレクトリンクを渡す。 ページ へのディープリンクを渡す。
    iOS Deep Link iOSアプリ内のページへのディープリンクを渡す。
    iOS Deferred Deep Link ユーザーがまだインストールしていないiOSアプリ内のページへの遅延ディープリンクを渡す。

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

グローバルプロパティ
#

Singular SDKでは、カスタムプロパティを定義し、 すべてのセッションおよびイベントと共に Singularサーバーへ送信できます。これらのプロパティは、 ユーザー情報、アプリモード/ステータス、その他任意の情報を 表すことができます。

  • 有効なJSONオブジェクトとして最大5つのグローバルプロパティを定義できます。 グローバルプロパティは、ブラウザのlocalStorage に クリアされるまで永続化されます。

  • 各プロパティ名と値は最大200文字までです。 これより長いプロパティ名または値を送信した場合、 200文字に切り詰められます。

  • グローバルプロパティは、Singularのユーザーレベルイベントログとポストバックに反映されます。

グローバルプロパティは、Google Tag Managerのトラッキングテンプレート初期化時にサポートされました。SingularGTM初期化タグタイプでは、プロパティのJSONオブジェクトを設定し、既存プロパティを上書きするかどうかを選択します。

GTMのグローバルプロパティタグタイプ

初期化時にグローバルプロパティを設定するには 初期化タグタイプを使用します。初期化後の実行時更新には 専用のグローバルプロパティタグタイプ(設定、取得、解除、クリア)を使用してください。

Set on InitializationSet Global PropertiesGet Global PropertiesUnset Global PropertyClear Global Properties

初期化タグの設定

SingularGTMのInitializationタグタイプでは、 `Global Properties `(オブジェクト)を `Key`および`Value `フィールドに 変数、データレイヤー値、またはテキストを割り当てることで設定します。 `Override `(true/false)フィールドは 必要に応じて調整してください。 `Override `が `false`の場合、既存のプロパティは変更されません。 `true`の場合、既存のプロパティは 上書きされます。

gpgtm.png


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

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

オーガニック検索トラッキング設定タグの作成

重要! - このタグはSingular SDKの初期化前に必ず発火させること!

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

この例は、オーガニック検索トラッキングを有効にするための 回避策 ソリューションとして提供されています。コードは あくまで 例として使用し、マーケティング部門のニーズに基づいて ウェブ 開発者が更新・保守を行う必要があります。 オーガニック 検索トラッキングは広告主ごとに異なる意味を持つ場合があります。 サンプルを 確認し、必要に応じて調整してください。

なぜこれを使用するのか?

  • キャンペーンパラメータが存在しない場合でも、 オーガニック検索からの訪問が適切に追跡されることを保証します。

  • 明確なアトリビューションのため、URLにwpsrcパラメータ(参照元)の値とwpcn パラメータ(キャンペーン名)を 「OrganicSearch」として付加します。

  • 現在のURLとリファラーをlocalStorageに保存し、 後で使用できるようにします。

  • 純粋なJavaScript、依存関係ゼロ、統合が容易。

動作原理

  1. ページURLから既知のキャンペーンパラメータ(Google、 Facebook、TikTok、UTMなど)をチェック。

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

    • wpsrc (リファラーを値として)
    • wpcn (値として OrganicSearch を設定)
  3. ページを再読み込みせずにブラウザのURLを更新します。

  4. 現在のURLとリファラーをlocalStoragesng_urlsng_ref として保存。

使用方法

  1. GTMで[タグ] > [新規]に移動し、 [タグ設定]をクリックして [カスタムHTML]を選択します。
  2. 以下の完全なJavaScriptコードを貼り付けます。
  3. このタグのトリガー作成はスキップします。 このタグはSingular Initタグの前に実行する必要があるため、 タグシーケンスを使用してInitタグより前に発火するよう設定します。

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

    • セットアップタグが最初に発火し、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デバイスIDを生成し、 ブラウザストレージを使用してこれを永続化します。このストレージはサブドメイン間で共有できないため、 SDKは各サブドメインごとに新しいIDを生成することになります。

サブドメイン間でSingularデバイスIDを永続化したい場合は、 以下のいずれかの方法を使用できます:

方法 B (上級者向け): Singular デバイス ID の手動設定
#

方法B(上級者向け):SingularデバイスIDの手動設定

Singular SDKによるデバイスIDの自動永続化を望まない場合、 トップレベルドメインクッキーやサーバーサイドクッキーなどを使用し、 ドメインを跨いでIDを手動で永続化できます。 値はSingularが事前に生成した 有効なuuid4形式のIDである必要があります。

注記:SingularデバイスIDは、カスタムJavaScript変数を定義し、 Initトラッキングタグ呼び出し後に singularSdk.getSingularDeviceId()を呼び出すことで取得できます。

mceclip2.png


GTM実装における一般的な問題点

必須のデバイス識別子
イベントが複数回発火する トリガーを調整または制限し、「1ページあたり1回」を使用するか条件を追加
製品IDの形式が不正 製品IDは逆DNS表記(com.company.site )を使用する必要があります
イベントが追跡されていない イベント名のスペルと大文字小文字を確認してください。singular イベントタグが実行される前にSDKが読み込まれていることを確認してください
SDKスクリプトがブロックされている 広告ブロッカーまたは制限的なコンテンツセキュリティポリシーが原因。問題が継続する場合はネイティブJS実装への移行を検討

ベストプラクティス

  • GTMを整理整頓:タグとトリガーに明確な名前を付け、 その目的を文書化する
  • 未使用またはレガシータグを定期的に監査する。
  • 重複イベントのリスク低減のため、トリガー数を最小限に抑える。
  • 変更後は、本番環境へ反映する前に必ずGTMのプレビューモードで徹底的にテストする。
  • クッキーベースのトラッキング(クロスサブドメイントラッキング)を使用する場合、 プライバシーポリシーを それに応じて更新する。

関連記事