静的サイトにアクセス解析を自作する Google Analytics不要の計測設計
この記事の要点
Supabaseを使って静的サイトのアクセスデータを自前で計測する仕組みを作れば、Google Analyticsのトラッキングスクリプトなしにページビュー・人気記事・流入元を把握できる。設計と実装を解説する。
結論
Supabaseを使った自前アクセス計測の最小構成は、page_viewsテーブルへのInsert + 管理ページでのSELECTだ。Google Analyticsのスクリプトを置き換えるには、計測の対象(何を記録するか)を決めて、クライアントサイドのJavaScriptから記録する実装を追加するだけだ。
計測する指標の設計
自前の計測で把握できる指標を設計する。
最低限(シンプルな実装で実現):
- 記事別のページビュー数
- 人気記事ランキング
- 総アクセス数
追加できる指標(実装が少し複雑になる):
- リファラー(流入元)
- 閲覧デバイスの種類(モバイル/デスクトップ)
- 国・地域(IPアドレスから)
- ページ滞在時間(ページ退出時に記録)
個人開発のメディアサイトであれば、「記事別PVと人気ランキング」だけでも十分な情報が得られる。
データベース設計
Supabaseのダッシュボード(SQL Editor)で実行する。
-- 詳細なアクセスログを記録するテーブル
CREATE TABLE access_logs (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
slug text NOT NULL,
referrer text,
device_type text CHECK (device_type IN ('mobile', 'tablet', 'desktop')),
created_at timestamptz DEFAULT now()
);
-- 記事別の集計テーブル(高速なランキング取得用)
CREATE TABLE page_views (
id bigint GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
slug text UNIQUE NOT NULL,
count integer NOT NULL DEFAULT 0,
updated_at timestamptz DEFAULT now()
);
-- RLS設定
ALTER TABLE access_logs ENABLE ROW LEVEL SECURITY;
ALTER TABLE page_views ENABLE ROW LEVEL SECURITY;
-- 全員がInsert可能(アクセス記録のため)
CREATE POLICY "Allow insert access_logs" ON access_logs
FOR INSERT WITH CHECK (true);
CREATE POLICY "Allow read page_views" ON page_views
FOR SELECT USING (true);
CREATE POLICY "Allow insert page_views" ON page_views
FOR INSERT WITH CHECK (true);
CREATE POLICY "Allow update page_views" ON page_views
FOR UPDATE USING (true);
UPSERTのSQL関数
アクセスのたびに呼び出す関数を作る。page_viewsテーブルへのUPSERT(なければInsert・あればUpdate)を1回のRPC呼び出しで行う。
CREATE OR REPLACE FUNCTION record_page_view(
page_slug text,
page_referrer text DEFAULT NULL,
page_device text DEFAULT 'desktop'
)
RETURNS void AS $$
BEGIN
-- 詳細ログに記録
INSERT INTO access_logs (slug, referrer, device_type)
VALUES (page_slug, page_referrer, page_device);
-- 集計テーブルをUPSERT
INSERT INTO page_views (slug, count)
VALUES (page_slug, 1)
ON CONFLICT (slug)
DO UPDATE SET
count = page_views.count + 1,
updated_at = now();
END;
$$ LANGUAGE plpgsql;
クライアントサイドの実装
Astroの記事ページに計測スクリプトを追加する。src/components/Analytics.astroを作成する。
---
interface Props {
slug: string;
}
const { slug } = Astro.props;
---
<script define:vars={{ slug }}>
import { supabase } from '../lib/supabase';
// 自分のアクセスをスキップ
if (localStorage.getItem('owner') === 'true') return;
// ボットの除外(簡易)
const botPatterns = /Googlebot|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot/i;
if (botPatterns.test(navigator.userAgent)) return;
// デバイスタイプの判定
const deviceType = window.innerWidth < 768 ? 'mobile'
: window.innerWidth < 1024 ? 'tablet'
: 'desktop';
// リファラーの取得(外部からの流入のみ記録)
const referrer = document.referrer && !document.referrer.includes(window.location.hostname)
? new URL(document.referrer).hostname
: null;
// アクセスを記録
await supabase.rpc('record_page_view', {
page_slug: slug,
page_referrer: referrer,
page_device: deviceType,
});
</script>
記事レイアウトから呼び出す:
---
import Analytics from '../components/Analytics.astro';
const { slug } = Astro.params;
---
<Analytics slug={slug} />
管理ページでの可視化
src/pages/admin/analytics.astroを作成する。
---
import { supabase } from '../../lib/supabase';
// 人気記事ランキング
const { data: topPages } = await supabase
.from('page_views')
.select('slug, count, updated_at')
.order('count', { ascending: false })
.limit(20);
// 今日のアクセス数
const today = new Date().toISOString().split('T')[0];
const { count: todayCount } = await supabase
.from('access_logs')
.select('*', { count: 'exact', head: true })
.gte('created_at', `${today}T00:00:00`);
// 流入元ランキング
const { data: referrers } = await supabase
.from('access_logs')
.select('referrer')
.not('referrer', 'is', null)
.gte('created_at', new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString());
---
<h1>アクセス解析</h1>
<section>
<h2>今日のアクセス数</h2>
<p>{todayCount} PV</p>
</section>
<section>
<h2>人気記事トップ20</h2>
<table>
<thead><tr><th>記事</th><th>累計PV</th></tr></thead>
<tbody>
{topPages?.map(p => (
<tr>
<td><a href={`/articles/${p.slug}/`}>{p.slug}</a></td>
<td>{p.count.toLocaleString()}</td>
</tr>
))}
</tbody>
</table>
</section>
プライバシーへの配慮
自前の計測でもプライバシーへの配慮が必要だ。
収集しない情報:IPアドレスの完全な記録・氏名・メールアドレス・ログインユーザーの行動の追跡(管理者が閲覧者を特定できないようにする)
プライバシーポリシーへの記載:自前のアクセス計測を行っている場合、プライバシーポリシーにその旨を記載することを推奨する。
Supabaseのアクセスカウンターの基本実装はSupabaseでアクセス数カウンターを作るで、管理ページの認証実装はSupabase Authでメール認証ログインを実装するで解説している。
まとめ
静的サイトの自前アクセス解析は、SupabaseのページビューテーブルとクライアントサイドのJavaScriptだけで実現できる。Google Analyticsより収集できる情報は限られるが、データを自社で保有できることとトラッキングブロッカーの影響を受けにくいことが利点だ。管理ページで人気記事ランキングと日次PVを確認できれば、コンテンツ戦略の改善に必要な情報は十分得られる。
よくある質問
なぜGoogle Analyticsを使わずに自前の計測を作るのですか
Google Analyticsはユーザーの行動データをGoogleが収集します。プライバシーへの配慮からトラッキングブロッカーでブロックされる割合も増えており、実際のアクセス数を過小計上する傾向があります。自前の計測はデータを自社で保有でき、EUのGDPR準拠の観点でも管理しやすい利点があります。
Supabaseでアクセス解析を作るとGoogle Analyticsと同じ機能が使えますか
Google Analyticsは多機能な分析プラットフォームで、自前の計測では全機能を再現することは難しいです。ページビュー数・人気記事・簡単な流入元程度であれば自前で実現できます。詳細な行動分析が必要な場合は、プライバシーファーストな代替サービス(Plausible・Umami等)との組み合わせも選択肢です。
計測データはどのくらいの期間保存できますか
Supabaseのデータベースに保存するため、Freeプランの500MB制限内であれば無期限に保存できます。1日1,000PVのサイトで1年分のアクセスデータは数MB程度のため、個人規模では容量の心配は不要です。
ボットのアクセスを除外する方法はありますか
User-Agentでのフィルタリング(Googlebot・Bingbotなど主要クローラーを除外)をクライアントサイドで行う方法が基本です。ただしクライアントサイドの除外は不完全で、ボットによってはJavaScriptを実行して計測されてしまうケースもあります。完全な除外にはサーバーサイドでの処理が必要です。