GTMのデータレイヤー変数が空で送られる罠。カスタムHTMLタグとネイティブタグの実行順レース

GTMのデータレイヤー変数が空で送られる罠。カスタムHTMLタグとネイティブタグの実行順レース
GTMでこういう構成を組んだことはないでしょうか。
- カスタムHTMLタグで値を作り、
dataLayer.push({my_id: '...'})する - その値をデータレイヤー変数(DLV)で受ける
- GoogleタグのGA4設定パラメータに
{{DLV - my_id}}として渡す
プレビューでは値が見えるのに、本番の計測ビーコンを見るとパラメータが空、あるいはパラメータ自体が消えている。今回ある案件でまさにこれを踏み、原因がタグの実行順にあると特定するまで少し遠回りをしました。実機検証の手順込みで残しておきます。
結論:カスタムHTML由来の値は、DLVではなくカスタムJavaScript変数で受ける
原因を一言でいうと、同じトリガーで発火するタグのうち、ネイティブタグ(Googleタグ等)はカスタムHTMLタグより先に実行されるからです。
- カスタムHTMLタグは、GTMがscript要素をページに注入して実行する方式です。注入は非同期で、実行までにわずかな遅延があります
- Googleタグ(GA4設定)はGTM内部のネイティブ実装で、トリガー発火と同時に同期的に評価されます
- 結果、初期化トリガーで「値を作るカスタムHTML」と「値を使うGoogleタグ」を並べると、GoogleタグがDLVを評価した時点でdataLayerにまだ値がない。変数は空で確定し、後からpushされても遡って反映されません

対処はシンプルで、値の受け渡しをdataLayer経由にしないことです。カスタムHTMLタグ側で値をlocalStorageやCookie、windowプロパティに保存し、受け側をカスタムJavaScript変数(CJS)にします。
function(){
try { var v = localStorage.getItem('my_id'); if (v) return v; } catch(e) {}
var m = document.cookie.match(/(?:^| )my_id=([^;]+)/);
if (m) return decodeURIComponent(m[1]);
return window.__my_id || undefined;
}
CJS変数はタグがパラメータを評価するその瞬間に同期実行されるので、ストレージに値さえあれば確実に拾えます。

実話:隣のパラメータは動いていたから、余計に気づきにくかった
今回ハマりが深くなった理由は、同じGoogleタグに既にあった別のカスタムパラメータが正常に動いていたことです。「同じタグの同じ設定テーブルなのに、なぜ新しい行だけ空なのか」と。
答えは変数の種類でした。動いていた既存行はCJS変数(localStorage直読)、新しく足した行はDLV。タグもトリガーも同じで、変数の評価方式だけが違った。DLVは「pushが済んでいるか」というタイミングに依存し、CJSは依存しない。この一点です。
なお「カスタムHTMLが先に実行されるようタグの順序付け(シーケンス)を設定する」という対処も理屈上はあります。ただ、タグ間の依存関係が設定の奥に埋まって後から読めなくなるので、私はストレージ+CJSの方が事故りにくいと思います。
検証は「プレビュー」で終わらせない
この種の問題はGTMプレビューだと見逃しやすいです。プレビューは変数の「現在値」を表示するので、画面を見た時点では値が入っているように見えます。確認すべきは「タグが発火した瞬間に何が送られたか」です。
実機での検証手順を載せておきます。
- 配信中のコンテナを直接確認する:
curl https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXXXの出力に、追加したパラメータ名が含まれるか。公開漏れ・キャッシュをまず潰す - 計測ビーコンを見る:実ページを開き、開発者ツールのNetworkで
/g/collectリクエストを確認。GA4のイベントパラメータはep.パラメータ名=値というクエリで載ります。ここに値が入って初めて成功です - ヘッドレスブラウザ(Playwright等)でこの確認を自動化しておくと、タグ公開のたびに30秒で回せます

今回も、公開直後のビーコン確認で「パラメータが載っていない」ことを検知し、CJS変数に差し替えた修正バージョンを重ねて公開、再度ビーコンで値を確認して完了としました。
アンチパターン:dataLayer.pushを「即時反映される共有変数」だと思う
dataLayerは共有メモリではなくイベントキューです。pushはGTMに「この値でイベントを処理して」と依頼する行為であり、既に評価が終わったタグの変数を書き換えることはありません。「pushしたのに空」系のトラブルは、ほぼすべて評価タイミングとpushタイミングの前後関係に帰着します。困ったら「この変数は、いつ、何をきっかけに評価されるか」を書き出すのが一番の近道です。
まとめ
- ネイティブタグはカスタムHTMLタグより先に実行される。カスタムHTMLがpushした値をDLVで受けると空になる
- カスタムHTML由来の値は、localStorage/Cookie/windowに保存してカスタムJavaScript変数で同期読み取りする
- 検証はプレビューではなく、配信中のgtm.jsと実機の計測ビーコン(ep.パラメータ)で行う
GTM・GA4の計測実装、タグ運用の自動化を支援しています。お問い合わせはこちら。
この記事のテーマに合うサービス:業務フロー自動化
スプレッドシート・メール・Slackの往復を、自動化で終わらせる

