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

- Name
- muzzledskr
参照元スパムの除去や 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"
httpRequestsAdaptiveGroups は 1クエリあたり最大1日。date を2回呼ぶ間の数秒で 24h をわずかに超えていた。窓を1日以内に収めれば通る。日次サマリー用途ならちょうどいい制約でもある。
出てきたのが 504 だった
窓を直して叩き直すと、ステータス別にこう出た(clientCountryName は 2 文字コードで返る)。
| ステータス | 件数 |
|---|---|
| 504 Gateway Timeout | 12 |
| 404 | 11 |
| 301 | 4 |
| 200 | 4 |
初回のクエリで 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.webmanifest | 504 | 9 |
| www.muzzledskr.dev/ | 522 | 7 |
| / | 504 | 3 |
| /tags/powerquery/ , /tags/inventory/ | 504 | 各2 |
| /about/ , /projects/ , /tags/ , /tags/sre/ … | 504 | 各1 |
ここで分かることが2つある。
- 504 が
//about//projects//tags/*と満遍なく出ている。 特定ページのバグではなく、サイト全体が断続的にタイムアウトしている ―― つまり基盤側の問題だ。 - 522 が出ている。 522 は「Cloudflare が origin に TCP 接続できなかった」ことを意味する。純粋な静的ホスティングなら本来出ないはずのステータスで、
wwwと apex で挙動が違うのも含めて、ルーティング / origin まわりを一度見直す必要があるというサインだった。
原因の切り分け自体はこの記事の範囲を超える(DNS と Pages の設定を別途追う)。ここで言いたいのは、この「落ちている」という事実に、GA4 経由では永久に気づけなかったという一点だ。
まとめ
- GA4 = 実行されたブラウザの表層。Cloudflare edge = 全リクエストの地層。 差分(ボット・スキャン・ブロック・5xx)に、"落ちている" が埋まっている。
- 監視対象はアプリの技術層だけではない。計測ビーコンに乗らないアクセスを、別系統(エッジログ)で拾う必要がある。
- 生ログの Logpush は Enterprise、Log Explorer は有料。だが "どのステータスがどの URL で出ているか" までなら GraphQL の集計(無料)で足りる。まずここから始めればいい。
次は、このクエリを日次で回して 5xx を検知・通知する仕組みにする。そしてその前に、522 の犯人を潰す。順番としては、たぶんそっちが先だ。
参考(一次情報)
Footnotes
Cloudflare Logs ― Logpush(HTTP リクエストの生ログ配信は Enterprise プラン): https://developers.cloudflare.com/logs/logpush/ ↩
Cloudflare Log Explorer ― FAQ(有料アドオン・データセット有効化時点から先のみ記録): https://developers.cloudflare.com/log-explorer/faq/ ↩
Cloudflare Analytics ― GraphQL Analytics API(全プランで利用可能): https://developers.cloudflare.com/analytics/graphql-api/ ↩
Cloudflare Fundamentals ― Audit Logs(version 2 と旧 API の併存): https://developers.cloudflare.com/fundamentals/account/account-security/audit-logs/ ↩