Astro ブログに GTM + GA4 を 30 分で導入する全手順|Cookie 同意バナー・アフィリクリック計測込み
本記事は実体験の記録です。アフィリエイトリンクは含みません。
- なぜ GA4 を直接ではなく Google Tag Manager(GTM)経由で入れるのか
- Astro の
<head>に GTM スニペットを埋め込む実装 - Cookie 同意バナー + Consent Mode v2 のシンプルな実装
- アフィリエイトリンククリックを GA4 に送るカスタムイベントの仕込み
結論:GTM 経由が結果的に一番楽だった
副業ブログを立ち上げるにあたり、最初は「GA4 のスニペットを直接 Astro に貼ればいい」と思っていました。でも実際にやってみると、後から 広告クリック計測 / コンバージョン / Hotjar / Cookie 同意 といった追加要素を入れるたびにコードを触る羽目になります。
夫(エンジニア)と相談した結果、最初から Google Tag Manager(GTM)を入口にする構成にしました。GTM だけ Astro に埋め込めば、その先に GA4・カスタムイベント・将来追加するタグを Web 管理画面から差し込めます。サイトのコードを再ビルドしなくて済むのが大きい。
- GTM(GTM-XXXXXXX)を Astro
<BaseHead>で配信 - Consent Mode v2 でデフォルト denied、Cookie 同意で update
- アフィリクリックは外部リンクの click を
dataLayer.push→ GTM で GA4 イベントに変換
1. Astro の <BaseHead> に GTM スニペットを入れる
Astro 公式 blog テンプレでは、src/components/BaseHead.astro に共通の <head> 要素がまとまっています。ここに GTM スニペットを追加します。
ポイントは 2 つ:
- 環境変数で GTM ID を切り替える:開発時はオフ、本番だけオンにできるようにする
- Consent Mode v2 を
gtm.jsロード前に設定:デフォルト denied で読み込んで、同意後に granted に切り替える
実際のコードはおおよそ次のようになります(簡略版)。
{import.meta.env.PUBLIC_GTM_ID && (
<script is:inline define:vars={{ gtmId: import.meta.env.PUBLIC_GTM_ID }}>
window.dataLayer = window.dataLayer || [];
function gtag() { window.dataLayer.push(arguments); }
window.gtag = gtag;
// デフォルトはすべて denied
gtag('consent', 'default', {
analytics_storage: 'denied',
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
functionality_storage: 'granted',
security_storage: 'granted',
});
// 過去に同意済みなら update
try {
if (localStorage.getItem('cookie_consent') === 'accepted') {
gtag('consent', 'update', {
analytics_storage: 'granted',
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
});
}
} catch (e) {}
// GTM 本体
(function (w, d, s, l, i) {
w[l] = w[l] || [];
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
var f = d.getElementsByTagName(s)[0],
j = d.createElement(s),
dl = l != 'dataLayer' ? '&l=' + l : '';
j.async = true;
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
f.parentNode.insertBefore(j, f);
})(window, document, 'script', 'dataLayer', gtmId);
</script>
)}
<noscript> の iframe は Header コンポーネントの <body> 直後に配置するか、Astro の場合は Header の中で出すと収まりが良いです。
.env に以下を書いて、本番ビルドだけ GTM が読まれる状態にしました。
PUBLIC_GTM_ID=GTM-XXXXXXX
2. Cookie 同意バナーの実装
Cookie 同意バナーは独立コンポーネントにしました。やることは少なくて、
- localStorage に
cookie_consentの値があるか確認 - なければバナー表示
- 「同意する」ボタンで
gtag('consent', 'update', {...granted...})を呼ぶ - 「拒否する」ボタンで
deniedを維持 - localStorage に保存して、次回以降はバナー非表示
function setConsent(value) {
localStorage.setItem('cookie_consent', value);
banner.classList.add('hidden');
if (value === 'accepted' && typeof window.gtag === 'function') {
window.gtag('consent', 'update', {
analytics_storage: 'granted',
ad_storage: 'granted',
ad_user_data: 'granted',
ad_personalization: 'granted',
});
}
}
GDPR 規制対応の細かい要件(拒否ボタンの表示位置、再同意の頻度など)は厳密にやり始めるとキリがないので、個人ブログのレベルでは「同意 / 拒否ボタンが両方出る」「拒否を選んだら計測が止まる」が満たせれば十分と判断しました。
3. アフィリクリック計測
外部リンクのクリックを GA4 にカスタムイベントとして送る仕組みを、Footer に小さなスクリプトで仕込みました。
document.addEventListener('click', function (e) {
const link = e.target && e.target.closest && e.target.closest('a');
if (!link || !link.href) return;
const isExternal = link.host && link.host !== location.host;
if (!isExternal) return;
const isAffiliate =
link.dataset.aff === '1' ||
/(a8\.net|moshimo|valuecommerce|afi-b\.com|accesstrade|impact)/i.test(link.href);
if (!isAffiliate) return;
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: 'aff_click',
affiliate_url: link.href,
affiliate_anchor: (link.textContent || '').trim().slice(0, 100),
affiliate_program: link.dataset.affProgram || '',
page_path: location.pathname,
});
});
ASP の代表ドメイン(A8.net、もしも、バリュコマ、afi-b、アクセストレード、Impact)を正規表現で判定。マッチしたリンククリックで aff_click という dataLayer イベントを発火します。
GTM 側で「カスタムイベント aff_click のトリガー」と「GA4 イベント affiliate_click のタグ」をペアで作って、Web 管理画面側で計測ロジックを完結させました。
4. GTM での設定
サイト側の仕込みが終わったら、GTM 管理画面で以下を順に作ります。
4-1. Google tag
すべての基盤になるタグ。
- タグタイプ: Google tag
- Tag ID:
G-XXXXXXXXXX(GA4 の測定 ID) - トリガー: Initialization - All Pages
Initialization トリガーは GTM の中でも最早期に発火するので、Consent Mode と相性が良いです。
4-2. データレイヤー変数
aff_click イベントで送られるデータを GTM 側で受け取るための変数を 2 つ作ります。
dlv - affiliate_url(データレイヤー変数名:affiliate_url)dlv - affiliate_program(データレイヤー変数名:affiliate_program)
4-3. カスタムイベント トリガー
- トリガータイプ: カスタム イベント
- イベント名:
aff_click - このトリガーの発生場所: すべてのカスタム イベント
- 名前:
Custom - aff_click
4-4. GA4 イベント タグ
- タグタイプ: Google アナリティクス: GA4 イベント
- 測定 ID: 上の Google tag を参照(または直接入力)
- イベント名:
affiliate_click - パラメータ:
affiliate_url/affiliate_program/link_url({{Click URL}}組み込み変数) - トリガー:
Custom - aff_click
最後に右上の「公開(Publish)」を押せば、新しいバージョンとして全サイトに反映されます。
つまずいたところ
「No Google tag found in this container」警告
最初に GTM で 「Google アナリティクス: GA4 Event」 を選んでしまい、警告が出ました。GA4 Event タグは「すでに Google tag が設定されているコンテナ」での追加イベント送信用なので、まず Google tag を作って公開しないと動きません。
正しい順序は Google tag → 公開 → GA4 Event です。
Initialization と All Pages の混在
Google tag のトリガーに Initialization - All Pages と All Pages の両方を設定していて、重複発火しそうな状態になりました。Google tag は Initialization のみが推奨で、All Pages トリガーはカスタムイベント側で使う想定。
動作確認
GA4 のリアルタイムレポートで、自分のセッションが見えれば計測オン成功です。確認時は シークレットウィンドウ + Cookie 同意「同意する」 がポイント。同意していないと Consent Mode が denied のままで GA4 にデータが送られません。
アフィリクリック計測の方は、ASP 提携承認 → 実 URL に置換 → 自分でクリックして affiliate_click イベントが発火するかを確認する流れになります。
まとめ
- 計測タグを増やすたびに Astro を再ビルドしなくて良い
- Cookie 同意の状態管理を Consent Mode に統一できる
- アフィリクリックを案件単位で追跡できる(特単交渉の根拠にも)
- 個人ブログから将来的に拡張しやすい
GTM・GA4・Cookie 同意バナーまで含めても、Astro 側の作業は 半日もかからず完了しました。GTM 管理画面の操作にやや慣れが要りますが、一度通すと「次のサイトでも同じ構成を使えそう」という再現性が出てきます。
このブログでも引き続き、副業立ち上げの実体験を記録していきます。次回はアフィリエイト ASP(A8 や もしも)への申請まわりを書く予定です。