Astro に GA4 を導入する|Cloudflare Pages の本番ビルドだけタグを出す(CF_PAGES_BRANCH)
この回でやること
連載「技術ブログの収益化準備」の第3回です。広告やアフィリエイトの効果を見るには、まずアクセス数が分からないと始まりません。そこで、Astro で作ったこのサイトに Google アナリティクス 4(GA4)のタグを入れました。
方針は次の3つです。
- タグは全ページ共通のレイアウトの
<head>に入れる - 測定ID(
G-から始まる ID)はsrc/consts.tsに書く。空ならタグを出さない - Cloudflare Pages で
mainブランチをビルドしたときだけタグを出す
3つ目は、手元での確認やプレビュー環境の閲覧が、本番のアクセス数に混ざらないようにするためです。
サイトの構成は、Astro(静的出力)+ microCMS + Cloudflare Pages です。全体像は総集編にまとめています。
前回の記事:プライバシーポリシーと Amazon アソシエイトの表記
本番ビルドかどうかを見分ける:CF_PAGES_BRANCH
Cloudflare Pages は、ビルドのときにいくつかの環境変数を自動で渡してくれます。その1つが CF_PAGES_BRANCH で、デプロイ中のブランチ名が入ります。
公式ドキュメントでも、使い道の例として「ブランチによってビルドを変える(本番ではデバッグログを出さない、など)」が挙がっています。
環境変数 | 値 |
|---|---|
| デプロイ中のブランチ名 |
参考:Build configuration | Cloudflare Pages docs
このサイトの本番ブランチは main です。CF_PAGES_BRANCH が main のときだけタグを出せば、次のビルドではタグが出なくなります。
- 手元での
npm run devやnpm run build(CF_PAGES_BRANCHが未設定) main以外のブランチを push したときのプレビューデプロイ
実装
1. astro:env で CF_PAGES_BRANCH を読めるようにする
このサイトでは、環境変数を Astro の astro:env で型付きで読み込んでいます。astro.config.mjs の env.schema に CF_PAGES_BRANCH を追加します。
env: {
// 未設定のままビルドすると、ここで分かりやすいエラーになる
schema: {
MICROCMS_SERVICE_DOMAIN: envField.string({ context: 'server', access: 'secret' }),
MICROCMS_API_KEY: envField.string({ context: 'server', access: 'secret' }),
// Cloudflare Pages がビルド時に自動で渡すブランチ名(ローカルでは未設定)
CF_PAGES_BRANCH: envField.string({ context: 'server', access: 'public', optional: true }),
},
},ポイントは2つです。
- 手元では未設定なので
optional: trueにする。これがないと、手元のビルドが「変数が足りない」エラーで止まる - ブランチ名は秘密の値ではないので
access: 'public'にする
astro:env の使い方は、連載「Astro × microCMS でブログを作る」の第1回で詳しく紹介しています。
参考:Using environment variables | Astro Docs
2. 測定IDを定数にする
GA4 の管理画面でウェブのデータストリームを作ると、G- から始まる測定IDが発行されます。これを src/consts.ts に書きます。
/** Google アナリティクス 4 の測定ID(G-XXXXXXXXXX)。空ならタグを出さない */
export const GA_MEASUREMENT_ID = 'G-XXXXXXXXXX';測定ID はタグとしてページの HTML にそのまま出る値なので、隠す必要はありません(後述)。
3. タグを出すコンポーネントを作る
GA4 のタグを出すコンポーネントです。条件を満たしたときだけ、2つの <script> を出力します。
---
import { CF_PAGES_BRANCH } from 'astro:env/server';
import { GA_MEASUREMENT_ID } from '../consts';
// Cloudflare Pages で main をビルドしたときだけタグを出す(ローカルやプレビューの閲覧を計測しない)
const enabled = CF_PAGES_BRANCH === 'main' && GA_MEASUREMENT_ID !== '';
---
{
enabled && (
<>
<script is:inline async src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`} />
<script is:inline define:vars={{ id: GA_MEASUREMENT_ID }}>
window.dataLayer = window.dataLayer || [];
window.gtag = function () {
window.dataLayer.push(arguments);
};
window.gtag('js', new Date());
window.gtag('config', id);
</script>
</>
)
}中身は、Google が案内している Google タグ(gtag.js)の設置コードを Astro 用に書き換えたものです。Google のコードは次の形です。
<script async src="https://www.googletagmanager.com/gtag/js?id=TAG_ID"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'TAG_ID');
</script>参考:Set up the Google tag with gtag.js | Google for Developers
書き換えたところを順に説明します。
is:inline:Astro に手を加えさせない
Astro は通常、<script> の中身を処理してバンドルします。外部のタグはそのまま HTML に出したいので、is:inline を付けて処理の対象から外しています。Astro のドキュメントでは、is:inline を付けると中身は処理・最適化・バンドルされないと説明されています。
define:vars:測定IDをスクリプトに渡す
define:vars を使うと、Astro 側の値(ここでは測定ID)を <script> の中で変数として使えます。ドキュメントによると、<script> に define:vars を付けると is:inline も暗黙に付きます。
参考:Template directives reference | Astro Docs
window.gtag に代入している理由
define:vars を付けたスクリプトは、ビルド後に関数で包まれて出力されます。本番サイトの HTML を見ると、次のようになっていました(改行は整形)。
<script>(function(){const id = "G-XXXXXXXXXX";
window.dataLayer = window.dataLayer || [];
window.gtag = function () {
window.dataLayer.push(arguments);
};
window.gtag('js', new Date());
window.gtag('config', id);
})();</script>Google のコードのように function gtag(){...} と書くと、gtag はこの関数の中だけで有効になり、ページの他の場所から呼べなくなります。そのため、window.gtag に代入してグローバルに置いています。dataLayer も同じ理由で window.dataLayer と明示しています。
4. 共通レイアウトの head に入れる
全ページ共通のレイアウトでコンポーネントを読み込み、<head> の中に置きます。
import GoogleAnalytics from '../components/GoogleAnalytics.astro';
// …(省略)…
<meta name="twitter:card" content={image ? 'summary_large_image' : 'summary'} />
<GoogleAnalytics />
</head>Google の案内では、<head> の開始タグの直後に貼るとされています。このサイトでは、メタタグを並べた後の <head> の最後に置いています。
ハマったこと:測定IDを .env に入れても反映されなかった
最初は、測定IDを .env に書きました。ところが、Cloudflare Pages の本番サイトにはタグが出ませんでした。理由は2つありました。
.envは.gitignoreで Git の管理から外しているので、GitHub に push されない。GitHub のリポジトリからビルドする Cloudflare Pages には届かない- そもそもコードは
src/consts.tsのGA_MEASUREMENT_IDを読む作りで、.envの値は使っていなかった
測定IDを src/consts.ts に書いてコミットしたところ、反映されました。
測定ID はタグの URL にそのまま入るので、ページのソースを見れば誰でも分かる値です。
$ curl -s https://cyber-blow.com/ | grep -oE 'gtag/js\?id=[A-Z0-9-]+'
gtag/js?id=G-XXXXXXXXXX隠す意味がないので、APIキーのように環境変数にせず、コードに直接書いています。環境変数で渡したい場合は、Cloudflare Pages の管理画面の環境変数に設定し、astro:env の schema にも宣言する必要があります(このサイトの microCMS の APIキーはその方法です)。
注意点
pages.dev の URL でもタグが出る
Cloudflare Pages の本番デプロイは、カスタムドメインのほかに 〜.pages.dev の URL でも見られます。どちらも main のビルドなので、pages.dev の URL にもタグが出ています。
$ curl -s https://<PROJECT>.pages.dev/ | grep -oE 'gtag/js\?id=[A-Z0-9-]+'
gtag/js?id=G-XXXXXXXXXXpages.dev 経由のアクセスも計測に入る点は、覚えておく必要があります。
タグが動いているかの確認
Google のドキュメントでは、ブラウザの開発者ツールで googletagmanager.com へのリクエストが出ていれば、タグが動作していると案内されています。本番サイトを開いて確認できます。
プライバシーポリシーへの記載
Google アナリティクスの利用規約では、プライバシーポリシーで Cookie の使用や Google アナリティクスの利用について開示することが求められています。このサイトの対応は前回の記事で紹介しています。
まとめ
- Cloudflare Pages はビルド時に
CF_PAGES_BRANCH(デプロイ中のブランチ名)を渡してくれる astro:envにoptional: trueで宣言し、mainのときだけ GA4 のタグを出した。手元のビルドやプレビューではタグが出ないdefine:varsを付けたスクリプトは関数で包まれるので、gtagはwindow.gtagに代入する- 測定IDは公開される値なので、
src/consts.tsに直接書いた。.envは Git に含めないので、Cloudflare のビルドには届かない
アドセンスの審査やアフィリエイトについては、実際に進めたらこの連載に回を追加していきます。