1. はじめに: 自律型ウェブ分析インフラの導入背景
SvelteKit Blog Engine(SKBE)は、Cloudflare PagesおよびD1をベースに動作するサーバーレスブログエンジンです。今回のSKBE v1.0.1.3アップデートでは、サードパーティの外部分析ツールに依存せず、Cloudflareインフラ内部でブログ訪問者統計を自立的に収集・保存・可視化できるCloudflare Web Analytics(CFA)統合ダッシュボードおよび分析データエクスポート機能を構築しました。
特に公開環境となる管理画面のセキュリティを担保するため、APIトークンを管理画面に保持させない閉鎖型独立同期Worker(cfa-sync)アーキテクチャを確立しました。さらに、エッジ集計ラグによる数値の逆行を防ぐ単調増加ガードや、蓄積されたデータをローカルに抽出できるCSV/XLSXダウンロード機能を実装しました。
2. Cloudflare Web Analytics総合ダッシュボードの構成
1) 直近28日間のトラフィックおよび4環境分析UI
管理画面ダッシュボード(apps/admin/src/routes/dashboard)上部に、Cloudflare Web Analytics専用ビューが表示されます:
- 直近28日間のトラフィックチャート: Chart.jsをベースに、ページビュー数(
Page Views)とユニーク訪問セッション数(Visits)を日別折れ線グラフで可視化します。 - 人気ページTop 5および流入元Top 5: 最も閲覧された記事パス(
requestPath)と外部流入ドメイン(refererHost)を集計してランキング表示します。リファラーヘッダーが存在しない直接アクセスはDirect / Noneとして欠損なく分類します。 - 4環境指標グリッド: 接続元の国(
countries)、利用デバイス(devices: Desktop, Mobile, Tablet)、ブラウザ(browsers)、オペレーティングシステム(os)の構成比を4枚のカードに要約出力します。
2) 日付別詳細フィルターおよび多言語対応
ダッシュボード上部のツールバーから、集計期間を即座に切り替えることができます:
今日/昨日/直近7日間クイック切り替え: ワンクリックで日別スナップショットデータを即座に更新します。特定日付選択 (YYYY-MM-DD)ドロップダウン: D1に蓄積された過去の特定日におけるディメンション統計を単独で照会できます。- すべての統計UIテキストは、日本語・韓国語・英語の3言語辞書(
t())に登録されており、管理者の言語設定に合わせてローカライズ表示されます。
3. セキュリティ分離アーキテクチャ: 攻撃サーフェスゼロ(Zero-Attack-Surface)の閉鎖型Worker
CFAデータを管理画面で表示する際、管理画面サーバー(+page.server.ts)からCloudflare GraphQL APIを直接呼び出す構成を採用すると、Cloudflare Pagesの環境変数にアカウントのCF_API_TOKENを常時保持させる必要が生じ、公開ランタイムにおける攻撃対象領域(Attack Surface)が増大するリスクがあります。
SKBEでは**「管理画面はトークンを持たず、D1の静的スナップショットのみを読み取る」**という単方向セキュリティ分離アーキテクチャを導入しました:
┌────────────────────────────────────────────────────────────────────────┐
│ Cloudflare Private Network (外部非公開の閉鎖ネットワーク) │
│ │
│ [Cloudflare Analytics] │
│ │ (GraphQL RUM API) │
│ ▼ │
│ [cfa-sync Worker] (workers_dev: false, クロンスケジューラ: 0 */1 * * *) │
│ │ (D1 Binding: INSERT / UPDATE) │
│ ▼ │
│ [D1 Database] ── (cf_daily_snapshots, visitor_stats) │
└─────────────┬──────────────────────────────────────────────────────────┘
│ (Read Only SELECT)
▼
┌────────────────────────────────────────────────────────────────────────┐
│ [Admin Pages] (skbeadmin.pages.dev) │
│ * CFA APIトークンを一切保持しない(セキュリティ分離の達成) │
│ * D1に集約されたスナップショットのみを安全に読み取りレンダリング │
└────────────────────────────────────────────────────────────────────────┘
- 外部公開アドレスの無効化 (
workers_dev: false):cfa-syncWorkerは外部インターネットからアクセス可能なドメインやHTTPルートを一切持ちません。外部からHTTPリクエストを送信できるエンドポイント自体が存在しないため、外部攻撃サーフェスは物理的に0です。 - Cloudflare内部クローンによる駆動:
Cloudflareシステム内部のスケジューラ(triggers: { crons: ['0 */1 * * *'] })により、毎時0分にバックグラウンドでのみ自動実行されます。 - D1スナップショットへの書き込み:
WorkerはGraphQL APIからデータを収集して正規化ハッシュを算出し、D1のcf_daily_snapshotsおよびvisitor_statsテーブルに保存して即時終了します。
4. データ整合性の担保: 分散集計ラグの防止と単調増加ガード
Cloudflare Web AnalyticsのrumPageloadEventsAdaptiveGroupsは、世界中のエッジノードから集約される分散OLAPデータセットです。エッジノード間の集計タイムラグや一時的な遅延を考慮し、同期エンジン(cfa-sync)にデータ整合性を維持するための保護機構を実装しました。
1) 当日データと過去データ(直近7日間)の処理基準の分離
- 過去データ(直近7日間の自動補正):
エッジ遅延集計の補正値を漏れなく反映するため、毎時実行時に直近7日間をまとめて取得します。ハッシュ値(data_hash)を比較し、過去値に変動が検知された場合のみD1レコードを最新値に更新(DO UPDATE)します。 - 当日データ(リアルタイム累積中):
現在進行形でアクセスが累積するデータであるため、集計タイムラグによって取得値が前回値より一時的に小さく算出される逆行現象を防ぐ単調増加ガードを適用します。
2) 当日データ逆行(カウント減少)防止ガードの実装
// apps/cfa-sync/src/index.ts
for (const snapshot of snapshots) {
const isToday = snapshot.date === todayStr;
const prevViews = existingViews.get(snapshot.date);
// 当日(Today)データ逆行防止ガード:
// 外部集計遅延などにより新取得値が既存D1値より小さく返ってきた場合、上書きをスキップして既存データを保護
if (isToday && prevViews !== undefined && snapshot.pageViews < prevViews) {
console.warn(`[CFA] Regression detected for today (${snapshot.date}): new(${snapshot.pageViews}) < existing(${prevViews}). Skipping update to protect data.`);
continue;
}
// データハッシュが変更された場合のみD1書き込みを実行
const hash = await sha256(JSON.stringify({ ... }));
if (existingHashes.get(snapshot.date) === hash) continue;
statements.push(env.BLOG_DB.prepare(`INSERT INTO cf_daily_snapshots ...`));
statements.push(env.BLOG_DB.prepare(`INSERT INTO visitor_stats ...`));
written++;
}
このガードにより、分散環境特有の集計シ差や一時的な遅延が発生した場合でも、すでに記録された当日の累積アクセス数が減少・汚染されることなくD1データの整合性が確実に保たれます。
5. 分析データエクスポート機能の実装: ワイドCSVおよび複数シートXLSX
1) データダウンロード機能の導入背景
画面上での閲覧にとどまらず、データバックアップ、外部報告用レポート作成、スプレッドシートでの2次集計・分析のため、D1データベースに蓄積された日別スナップショット(cf_daily_snapshots)をローカルファイルとして直接抽出できる専用エクスポート機能を新設しました。
2) ダウンロードインターフェースとオプション設計
ダッシュボード上部のプロバイダツールバーにCSV/XLSXエクスポートボタンを配置し、クリック時に詳細設定モーダルが表示されます:
- 期間選択:
直近7日間,直近28日間,今月,直近90日間 (四半期),1年間決算 (年間) - レポート形式の選択:
- 総合分析レポート (CSV): トラフィック推移と主要5大ディメンションを1つのレポートに統合出力
- カスタム項目選択 (XLSX): 必要な項目のみを選択し、個別シートにタブ分割して出力
- 抽出項目チェックボックス:
日別トラフィック推移,人気ページ,流入元,接続元の国,ブラウザ,端末タイプ
3) 2つのフォーマット別アーキテクチャ
A. 総合分析レポート: 日別1行圧縮ワイドCSV規格
集計表の途中にテキストや別フォーマットの見出しが混ざると、Excelの自動フィルターやピボットテーブル集計が破綻します。これを防止するため、完全に正規化された単一テーブル構造を採用しました:
- 圧縮ワイド列構造:
日付, ユニーク訪問者, ページビュー, 主要流入元 (Top 3), 人気ページ (Top 3), 接続国 (Top 3), 主ブラウザ (Top 2), 主デバイス (Top 2) - 年間決算の高速生成: 365日間の広範な期間を選択した場合でも、D1スナップショットを日別1行に圧縮結合し、過不足なく正確に365行の単一データ表として高速出力します。
- Excel文字化け防止: 先頭にUTF-8 BOM(
\uFEFF)を付与し、Microsoft Excelで開いた際にも日本語やCJK文字が文字化けしないエンコーディングを保証します。
B. カスタム項目選択: SheetJS連携による複数シートXLSX抽出
単一のフラットCSVにおいて、異なる性質のディメンション(ページURL、国名、ブラウザ名など)を同一列に混在させると、データの並び替えや集計が困難になります。この課題を解決するため、**SheetJS (xlsx)**エンジンをサーバーに統合しました:
- 独立シート分割: 選択された項目ごとに
トラフィック推移,人気ページ,流入元,接続国,ブラウザ,デバイスの各タブに分離生成されます。 - シート別最適化ヘッダー: 人気ページシートはPV数(
views)重視、流入元・国・端末シートは訪問者数(visitors)重視など、シートの性質に応じた最適な列構造が自動適用されます。 - エッジランタイム動作: Node.jsネイティブのファイルシステムバインディングを使用せず、Cloudflare Workers内部の純粋なメモリバッファ(
type: 'buffer', bookType: 'xlsx')上でバイナリファイルを高速生成します。
4) 多言語辞書連携とダウンロード安全性
- 中央集権型i18n辞書連携: 列ヘッダーおよびシート名はハードコードを排除し、
@blog/shared/i18nのfallbackDictionaryを参照して管理者の設定言語(日本語・韓国語・英語)に合わせて動的に翻訳されます。 - 動的Content-Dispositionおよび拡張子の付与: サーバーの
Content-Dispositionヘッダーとフロントエンドのダウンロードアンカー(a.download)を連動させ、選択モードに応じてskbe_analytics_28d_2026-10-31.csvまたは.xlsxが正確に割り当てられます。
6. 実務ガイド: マルチアカウントデプロイコマンドとパイプライン
SKBEは、単一の開発リポジトリから複数の独立したCloudflareアカウント(メインブログ、サブブログなど)を運用できるマルチアカウントデプロイパイプラインを標準搭載しています。
1) 設定ファイルの構成 (.deploy-accounts.json)
プロジェクトルートの.deploy-accounts.jsonに、各アカウントのD1、Pagesプロジェクト名、CFA同期設定(cfa)を定義します:
{
"main": {
"name": "メインブログ",
"token": "Cloudflare_APIトークン(メインアカウント用)",
"accountId": "Cloudflare_アカウントID",
"blogProjectName": "skbeblog",
"adminProjectName": "skbeadmin",
"blogDb": {
"name": "skbeblog-db",
"id": "D1_データベースUUID"
},
"cfa": {
"cfApiToken": "CFA読み取り権限を持つAPIトークン",
"cfSiteTag": "Web_Analyticsサイトタグ",
"workerName": "skbe-cfa-sync"
}
},
"second": {
"name": "サブブログ",
"token": "サブアカウント用APIトークン",
"accountId": "サブアカウントID",
"blogProjectName": "second-blog",
"adminProjectName": "second-admin",
"blogDb": {
"name": "second-blog-db",
"id": "サブD1_UUID"
},
"cfa": {
"cfApiToken": "サブCFA用APIトークン",
"cfSiteTag": "サブサイトタグ",
"workerName": "second-cfa-sync"
}
}
}
2) デプロイコマンドリファレンス
A. マルチアカウント統合デプロイ (deploy:multi)
指定したアカウントのブログ、管理画面、CFA Workerを順次ビルド・デプロイします。
# 特定アカウントの全資産(ブログ + 管理画面 + CFA Worker)を一括デプロイ
npm run deploy:multi <アカウントキー>
# 例: npm run deploy:multi main
# 例: npm run deploy:multi second
# 特定アカウントの特定アプリのみデプロイ (blog または admin)
npm run deploy:multi <アカウントキー> blog
npm run deploy:multi <アカウントキー> admin
# 登録されている全アカウントを順次一括デプロイ
npm run deploy:multi --all
- 実行フロー:
- 対象アカウントD1のマイグレーション自動実行(
schema-blog-db.sql,schema-user-db.sql) - SvelteKitアプリのビルドおよびCloudflare Pagesへのデプロイ
cfa設定が存在する場合、cfa-syncWorkerの自動デプロイとSecrets注入
- 対象アカウントD1のマイグレーション自動実行(
B. CFA Sync Worker単独デプロイ (deploy:cfa-sync)
ブログや管理画面の再ビルドを行わず、CFA Workerコードのみを単独更新するコマンドです。
# 登録アカウントのCFA設定状態一覧を表示
npm run deploy:cfa-sync
# 特定アカウントのCFA Workerを単独デプロイ
npm run deploy:cfa-sync -- <アカウントキー>
# 例: npm run deploy:cfa-sync -- main
# 全アカウントのCFA Workerを一括デプロイ
npm run deploy:cfa-sync -- --all
C. 単一デフォルトデプロイ (deploy:admin, deploy:blog)
単一アカウント環境で使用する標準コマンドです。deploy:admin実行時には、メインアカウントのCFA Workerデプロイ手順が自動的に連動して実行されます。
# メイン管理画面のデプロイ + メインCFA Workerの自動プロビジョニング
npm run deploy:admin
# メインブログのデプロイ
npm run deploy:blog
3) 重複デプロイ防止と強制更新 (--force フラグ)
不要なCloudflare API呼び出しを削減するため、deploy-cfa-sync.jsはデプロイ前にWorkerの存在確認を実施します:
⏩ [main] [JA] CFA Sync Worker ("skbe-cfa-sync") はすでに稼働中です。デプロイをスキップします。
[JA] 強制再デプロイが必要な場合: --force
- スキップ条件: 対象Cloudflareアカウントに同名のWorkerがすでに存在する場合、デプロイは自動的にスキップされます。
- 強制再デプロイ (
--force):apps/cfa-sync/src/index.tsを修正し、最新コードを即時反映させる場合は--forceフラグを付与して実行します:npm run deploy:cfa-sync -- <アカウントキー> --force # 例: npm run deploy:cfa-sync -- main --force
7. おわりに
SKBE v1.0.1.3アップデートにより、外部トラッキングスクリプトや重量級の依存関係を持たずに、Cloudflare自体のインフラ機能のみを活用した高精度かつ安定したアクセス解析環境が完成しました。
閉鎖型分離Workerモデルによるトークン保護、分散集計シ差を吸収する単調増加ガード、そして純粋なインメモリCSV/XLSXエクスポートエンジンの統合により、サーバーレスエッジ環境におけるデータの完全性と運用の柔軟性を高い次元で両立しています。
コメント 0件
コメントを投稿するにはログインが必要です。