Published on

GA4 に映らないアクセスを Cloudflare GraphQL で拾う ― 自ブログの 504/522 を edge ログから見つけるまで

Authors
  • avatar
    Name
    muzzledskr
    Twitter

参照元スパムの除去や GA4 の設定記事は世に多い。本記事はそれではない。扱うのは GA4 が「構造的に」観測できない層 ―― JavaScript が実行されないアクセス(ボット・スキャナ・そもそもエラーで返っているリクエスト)を、Cloudflare のエッジログ側から拾う一点に絞る。

そして正直に書くと、これを実際にやってみたら 自分のブログが 504 / 522 で落ちていた。GA4 のダッシュボードは、その間ずっと静かだった。調べながらの記録として残す。

なぜ GA4 では 5xx が見えないのか

GA4 は JS ビーコン方式だ。ページが 200 で返り、ブラウザが計測タグを実行して初めてイベントが記録される。裏を返すと:

  • ページが 504 / 522 / 500 で返った → JS は走らない → GA4 に乗らない
  • ボット / スキャナ / curl → JS を実行しない → 乗らない
  • WAF にブロックされたリクエスト → そもそも到達しない → 乗らない

つまり「サイトが落ちている」「機械的に叩かれている」という事象は、GA4 では原理的にほぼ観測できない。ここを埋めるには、ブラウザではなく エッジ(全リクエストが通る層) のログを見るしかない。

Cloudflare でログを取る3つの道(と料金)

自ドメイン(Cloudflare Pages + 独自ドメイン)で使える選択肢は3つ。ここは料金が絡むので一次情報で押さえた。

手段中身料金
Logpush生ログを S3/R2 等へ配信Enterprise 限定1
Log Explorer生ログを SQL で照会有料アドオン・遡及なし2
GraphQL Analytics API集計値を取得全プランで利用可(無料)3

「全アクセスの生ログを SQL で」まで欲しければ Log Explorer(有料)だが、"どのステータスがどれだけ・どの URL で出ているか"を知るだけなら GraphQL の集計で足りる。まずは無料の GraphQL で十分だった。

余談。アカウントの監査ログ API は endpoint が2系統ある。旧 GET /accounts/{id}/audit_logs(v1)と新 GET /accounts/{id}/logs/audit(v2)で、返るフィールド構造が違う4。パスだけ v2 にしてフィールドは v1 のまま、で一度ハマった。ログ API を触るときは「どのバージョンの、どのスキーマか」を最初に確定させたほうがいい。

GraphQL を curl で叩く

エンドポイントは1つ、POST https://api.cloudflare.com/client/v4/graphql。body は {"query": "...", "variables": {...}} の JSON だ。

curl -s -X POST "https://api.cloudflare.com/client/v4/graphql" \
  -H "Authorization: Bearer $CF_API_TOKEN" \
  -H "Content-Type: application/json" \
  --data @- <<'JSON'
{
  "query": "query($zoneTag:String!,$since:Time!,$until:Time!){viewer{zones(filter:{zoneTag:$zoneTag}){httpRequestsAdaptiveGroups(limit:10,filter:{datetime_geq:$since,datetime_lt:$until}){count dimensions{edgeResponseStatus clientCountryName clientRequestHTTPHost}}}}}",
  "variables": { "zoneTag": "<ZONE_ID>", "since": "...", "until": "..." }
}
JSON

最初、24時間ぶんを投げたらこう返ってきた。

"cannot request a time range wider than 1d, but your query time range spans 1d22s"

httpRequestsAdaptiveGroups1クエリあたり最大1日date を2回呼ぶ間の数秒で 24h をわずかに超えていた。窓を1日以内に収めれば通る。日次サマリー用途ならちょうどいい制約でもある。

出てきたのが 504 だった

窓を直して叩き直すと、ステータス別にこう出た(clientCountryName は 2 文字コードで返る)。

ステータス件数
504 Gateway Timeout12
40411
3014
2004

初回のクエリで 504 が 12 件。当然ながら、同じ期間の GA4 には一件も出ていない。パイプラインを組む前に、監視の存在意義が先に証明された格好だ。

URL 別に掘る(path は無料でも取れた)

「無料プランでは clientRequestPath(URL)が取れないことがある」という報告を見ていたので半信半疑だったが、このゾーンでは取れた。5xx を edgeResponseStatus_geq: 500 で絞り、clientRequestPath を dimension に足す。

httpRequestsAdaptiveGroups(
  limit: 20, orderBy: [count_DESC],
  filter: { datetime_geq: $since, datetime_lt: $until, edgeResponseStatus_geq: 500 }
) { count dimensions { edgeResponseStatus clientRequestHTTPHost clientRequestPath } }

返ってきた実データ(抜粋):

URLステータス件数
/static/favicons/site.webmanifest5049
www.muzzledskr.dev/5227
/5043
/tags/powerquery/ , /tags/inventory/504各2
/about/ , /projects/ , /tags/ , /tags/sre/ …504各1

ここで分かることが2つある。

  1. 504 が / /about/ /projects/ /tags/* と満遍なく出ている。 特定ページのバグではなく、サイト全体が断続的にタイムアウトしている ―― つまり基盤側の問題だ。
  2. 522 が出ている。 522 は「Cloudflare が origin に TCP 接続できなかった」ことを意味する。純粋な静的ホスティングなら本来出ないはずのステータスで、www と apex で挙動が違うのも含めて、ルーティング / origin まわりを一度見直す必要があるというサインだった。

原因の切り分け自体はこの記事の範囲を超える(DNS と Pages の設定を別途追う)。ここで言いたいのは、この「落ちている」という事実に、GA4 経由では永久に気づけなかったという一点だ。

まとめ

  • GA4 = 実行されたブラウザの表層。Cloudflare edge = 全リクエストの地層。 差分(ボット・スキャン・ブロック・5xx)に、"落ちている" が埋まっている。
  • 監視対象はアプリの技術層だけではない。計測ビーコンに乗らないアクセスを、別系統(エッジログ)で拾う必要がある。
  • 生ログの Logpush は Enterprise、Log Explorer は有料。だが "どのステータスがどの URL で出ているか" までなら GraphQL の集計(無料)で足りる。まずここから始めればいい。

次は、このクエリを日次で回して 5xx を検知・通知する仕組みにする。そしてその前に、522 の犯人を潰す。順番としては、たぶんそっちが先だ。

参考(一次情報)

Footnotes

  1. Cloudflare Logs ― Logpush(HTTP リクエストの生ログ配信は Enterprise プラン): https://developers.cloudflare.com/logs/logpush/

  2. Cloudflare Log Explorer ― FAQ(有料アドオン・データセット有効化時点から先のみ記録): https://developers.cloudflare.com/log-explorer/faq/

  3. Cloudflare Analytics ― GraphQL Analytics API(全プランで利用可能): https://developers.cloudflare.com/analytics/graphql-api/

  4. Cloudflare Fundamentals ― Audit Logs(version 2 と旧 API の併存): https://developers.cloudflare.com/fundamentals/account/account-security/audit-logs/