Google Analytics を入れずにブログ群を運用する — Cloudflare GraphQL Analytics と GitHub Actions による監視設計
アクセス解析を入れるとなると、まず GA4(Google Analytics 4)の測定タグを貼る、というのが多くの現場の初期設定です。ですが、複数のブログを運用していて「まず知りたいのは、ちゃんと配信できているか・壊れていないか」だとすると、GA4 だけでは足りず、そして GA4 でなくても取れる情報がかなりあります。
このサイトを含む複数のブログは、GA4(gtag.js)を一切入れていません。代わりに、Cloudflare のエッジ計測(GraphQL Analytics API)と Google Search Console、それを回す GitHub Actions の 3 つで運用の可視化をまかなっています。
本記事は「GA を捨てろ」という話ではありません。アクセス解析には「クライアント側で計る層」と「エッジ(配信側)で計る層」という別レイヤーがあり、用途によっては後者だけで運用が回る、という整理です。実際に動かしている構成と、GA との役割分担、ハマりどころをまとめます。
前提: サイトを Cloudflare(Workers / Pages)で配信していることが条件です。エッジ計測は Cloudflare がリクエストを受けている前提で成り立ちます。
GA4 とエッジ計測は「測っている場所」が違う
同じ「アクセス解析」でも、GA4 とエッジ計測は測定点がまったく違います。ここを混ぜると議論が噛み合いません。
GA4(クライアント側 JS 計測) は、ブラウザで gtag.js を実行して初めてデータが飛びます。だから取れるものと取れないものがはっきりしています。
- 取れる: ページ内のスクロールやクリック、滞在時間、コンバージョン、参照元、擬似的なユーザー単位の行動
- 取れない / 弱い: JS を実行しないボット、スクリプトブロッカーやトラッキング拒否の環境(そのぶん欠測する)
- コスト: スクリプトの読み込み・実行がフロントに乗る(パフォーマンス面の負荷)
- 論点: Cookie や識別子を使うため、同意バナーや外部送信規律への対応を検討する必要が出てくる(EU の GDPR/ePrivacy、日本では改正電気通信事業法の外部送信規律が 2023 年 6 月に施行。詳しくは後述の一次情報を参照)
エッジ計測(Cloudflare) は、Cloudflare のエッジに届いた全リクエストをサーバー側で記録します。クライアントには何も追加しません。
- 取れる: ボット込みの生トラフィック、ステータスコード別の件数(404 や 5xx などの障害系がそのまま見える)、ホスト別・パス別の内訳
- 取れない: ページ内の行動、滞在時間、コンバージョン、「人間の読者が何人か」の絶対値
- コスト: クライアントに一切スクリプトを足さない(Cookie レス。同意バナーの論点がそもそも発生しない)
この 2 つは代替関係ではなく、見ている面が直交しています。「読者がどう読んだか」を知りたいなら GA4、「配信が壊れていないか・どこに穴が空いているか」を知りたいならエッジ計測、という住み分けです。運用の一次監視として欲しいのは後者だった、というのがこの構成の出発点です。
手順1: Cloudflare GraphQL Analytics で「配信の生データ」を引く
Cloudflare は GraphQL Analytics API を提供しています。エンドポイントは 1 つ(https://api.cloudflare.com/client/v4/graphql)で、ここに POST でクエリを投げます。
アカウント全体の HTTP トラフィックは httpRequestsAdaptiveGroups(データセット一覧)で取れます。ホスト別・ステータス別に絞り込めるので、複数サイトを 1 回のクエリで横断できます。
query ($acc: string, $since: Time, $until: Time) {
viewer {
accounts(filter: { accountTag: $acc }) {
notFound: httpRequestsAdaptiveGroups(
filter: {
datetime_geq: $since, datetime_lt: $until,
edgeResponseStatus: 404, requestSource: "eyeball"
}
limit: 500, orderBy: [count_DESC]
) {
count
dimensions { clientRequestHTTPHost clientRequestPath }
}
}
}
}ポイントは 2 つあります。
- `requestSource: "eyeball"`: 「エッジに実際に届いたリクエスト」に限定するフィルタです。これを付けないと内部的なリクエストも混ざります。
- `edgeResponseStatus`:
404指定やedgeResponseStatus_geq: 500(5xx 以上)でステータスを直接絞れます。これが GA4 では取りづらい、「エラーを返したリクエスト」そのものを数えるという視点です。
このクエリを Node の標準 fetch で叩き、ホスト別に requests / 404 / 5xx を集計、前期比(前週比)を添えて週次レポートにしています。5xx が出ているパス、そしてスキャナのノイズを除いた「対処すべき本物の 404」の上位を並べる、というのが実運用の中身です。
curl -s https://api.cloudflare.com/client/v4/graphql \
-H "Authorization: Bearer $CLOUDFLARE_ANALYTICS_TOKEN" \
-H "Content-Type: application/json" \
--data @query.json無料プランの場合、httpRequestsAdaptiveGroups の保持期間はおよそ 30 日です。長期のトレンドを追う用途には向きませんが、「直近で壊れていないか」の定点観測には十分です。長期のトレンドは Search Console 側で見ます(後述)。
手順2: 読み取り専用トークンを分離する
このレポートに必要な権限は読み取りだけです。デプロイ用の万能トークンを使い回すべきではありません。Cloudflare の API トークンを、監視専用に別発行します。
- Account Analytics: Read(GraphQL Analytics を読むのに必要)
- Account Settings: Read(トークンからアカウント ID を引くのに必要)
この 2 つだけを持つトークンを作り、CLOUDFLARE_ANALYTICS_TOKEN として監視ジョブに渡します。デプロイ用の CLOUDFLARE_API_TOKEN(書き込み権限を持つ)とは別物として分けるのが定石です。監視のために漏れると困るトークンの権限範囲を、最初から最小にしておきます。
手順3: 監視者を「被監視系の外」に置く — GitHub Actions
ここが設計上いちばん大事なところです。Cloudflare を監視する仕組みを、Cloudflare の中に置いてはいけません。
Cloudflare Workers の Cron Triggers だけで死活監視を組むと、Cloudflare 自体が落ちたときに監視も一緒に沈黙します。監視者は、被監視系とは別の障害ドメインに置く必要があります。ここでは GitHub Actions の schedule(cron)を監視者にしています。「Cloudflare が落ちたら GitHub 側のジョブが失敗して気づく」という、障害ドメインの分離です。
実際のジョブは 2 系統です。
- 死活チェック(1 日 2 回): 全サイトのトップページを GET し、最終ステータス 200 を確認する。一時的なネットワーク断でのフラッピングを避けるため、失敗分だけ 30 秒待って 1 回リトライする。失敗したときだけ Slack に通知し、ジョブを異常終了させる。
- 週次レポート(週 1 回): 手順 1 のクエリを回し、ホスト別トラフィックと 404 / 5xx を Slack に投稿する。さらに対処候補(5xx か本物の 404)があった週だけ、GitHub Issue を自動起票する。
on:
schedule:
- cron: "0 0,12 * * *" # JST 9:00 / 21:00 → 死活チェック
- cron: "0 23 * * 0" # 月曜 JST 8:00 → 週次レポートこの Issue 起票がキモで、検出されたエラーをそのまま「作業チケット」に変換しています。起票された Issue を、後日セッションで「これ対処しといて」と AI エージェントに渡す運用です。監視が「Slack を見て人が気づく」で止まらず、対処待ちキューとして積まれるので、取りこぼしにくくなります。
実際にこのレポートで見つかったもの
このエッジ計測は、GA4 なら埋もれていたであろう障害を実際に掘り出しています。
- 全サイトで sitemap が 500 を返していた。5xx フィルタが
/sitemap.xmlを上位に出して発覚。GA4 は「読者が来なかった」ことは示しても、「クローラが 500 で弾かれていた」ことは教えてくれません。 - あるサイトで 404 が週あたり +3,743 件に急増。前週比の差分が異常値として立ち、パス一覧から原因を特定できました。
- 記事内の画像が 404。本文からリンクされた画像パスが 404 上位に並び、リンク切れとして見つかりました。
いずれも「配信側のステータスコード」を数えているから見えるもので、クライアント側の行動計測(GA4)とは検出できる障害の種類が違う、という具体例です。
エラーに見えて正常・逆にハマる点
/404 への 302 が 404 件数の大半を占める
各サイトは存在しない URL を /404 に 302 リダイレクトする実装になっています。すると Cloudflare の 404 集計には、`/404` ページ自体へのアクセスが大量に混ざります。これはリダイレクト先なので「対処すべき 404」ではありません。集計時に /404 を別枠に切り出し、ノイズ(スキャナが総当たりする wp-login、.env、.git などのパス)も正規表現で除外して初めて、「本当に直すべき 404」が見えます。生の 404 件数をそのまま眺めても意味がありません。
なお、この「/404」への 302 方式そのものをやめ、Astro.rewrite で元 URL を保ったまま正しく 404 を返す設計は「Astro SSR で存在しない URL に正しく 404 を返す」にまとめています。
Bot Fight Mode が監視リクエスト自体を弾く
Cloudflare の Bot Fight Mode を有効にしていると、GitHub Actions からの死活チェックが 403 で弾かれることがあります。監視リクエストは「データセンター IP + 非ブラウザ UA」という、ボットそっくりの特徴を持つためです。死活チェックが謎の 403 を出し続けるときは、まず監視元 IP / UA が Bot Fight Mode に引っかかっていないかを疑ってください。
検索流入の分析は Search Console が担う
エッジ計測に無いのは「どんな検索クエリで、何位で、どれだけ入ってきたか」です。ここは GA ではなく Google Search Console が担当します。GA4 の代替は「Cloudflare エッジ計測 + Search Console」の組み合わせだ、という理解が正確です。
Search Console API をスクリプトから叩いて定点レポート化する話は、別記事にまとめています。2026 年時点ではサービスアカウント追加が Google 側バグで詰まるので、回避策も含めて参照してください: 「サービスアカウントが Search Console に追加できない(email not found)— Google 側バグと gcloud ADC での回避」。
この構成の割り切り
GA なしで成立する用途と、しない用途を正直に書いておきます。
- 成立する: 「配信が壊れていないか」「どのページが 404/5xx になっているか」「ホスト別にどれくらいリクエストが来ているか」「検索でどう拾われているか」を知りたい運用。ブログ群の健全性監視と SEO 観測は、この構成で回ります。
- 成立しない / GA を足すべき: ページ内の行動導線、滞在時間、コンバージョン率、ファネル分析、A/B テストの効果測定。「人間の読者がどう振る舞ったか」を測りたいなら GA4(や同種のクライアント計測)が要ります。エッジ計測は「人間の読者数の絶対値」にはなりません(ボット込みの生トラフィックだからです)。
「まず配信の健全性、必要になったら行動計測を足す」という順番なら、初期は GA を入れずにエッジ計測 + Search Console で始めて問題ありません。同意バナーや外部送信規律の対応コストを、必要になるまで先送りできるのも利点です。
まとめ
- アクセス解析にはクライアント計測(GA4)とエッジ計測(Cloudflare)という別レイヤーがある。前者は行動・滞在・CV、後者は生トラフィックと 404/5xx が見える
- Cloudflare の
httpRequestsAdaptiveGroups(requestSource: "eyeball")を GraphQL で叩けば、クライアントにスクリプトを足さずに配信の生データが取れる。無料プランの保持は約 30 日 - トークンは監視専用に読み取り権限だけ(Account Analytics: Read + Account Settings: Read)で別発行する
- 監視者は被監視系の外(GitHub Actions)に置く。Workers Cron だけだと Cloudflare ごと落ちたとき沈黙する
- 検索流入は Search Console が担う。GA4 の代替は「エッジ計測 + Search Console」の組み合わせ
- ページ内行動・滞在・CV を測りたくなったら、そこで初めて GA4 を足せばよい