如何在 Cloudflare Pages 上使用流量統計功能 (基於 Cloudflare API )
上個月我在幫部落格加上一個「即時流量統計」與「AI 爬蟲造訪次數」的側邊欄元件時,遇到了一個麻煩:既然整個部落格是部署在 Cloudflare Pages 上的純靜態網頁(Static Site),我該怎麼在不洩露 API token 的前提下,撈到後台的 GraphQL Analytics 資料?這篇文章記錄了我如何利用 Pages Functions 當成 Proxy,在同源(Same-origin)的安全保護下,把 Cloudflare GraphQL 的流量統計資料安全地送到前端展示。
這個流程的通訊邏輯很單純:
靜態網站部署到 Cloudflare Pages 之後,透過把 token 放在 Pages 的環境變數,然後用 Pages Function 做一層同源 API proxy 做轉發。
透過轉發帶 token 去查 Cloudflare GraphQL Analytics API。
這個流程圖簡單說明了前端與後端的通訊邏輯:前端打同源 API,由 Pages Function 幫忙帶上敏感的 token,再去背後的 Cloudflare API 撈資料。
這樣做能維持純靜態部署的優點,不需要維護任何實體伺服器,又能透過 Edge Function 即時跟 Cloudflare 的統計後台撈資料。
---
這個架構需要準備哪些環境變數?
在極簡的架構下,你只需要準備以下兩個參數:
CF_API_TOKEN
CF_ZONE_ID
這兩個是 Cloudflare Pages 專案裡最重要的環境變數,一個是帶有 Analytics 讀取權限的 token,另一個則是你的網域 ID。
CF_API_TOKEN 請務必設定在 Cloudflare Pages 的 Encrypted secret。這個 token 所需的最低權限為 Account Analytics: Read 即可。千萬不要把 token 直接寫進 static/、public/、前端 JS 或 commit 到 GitHub 裡。
CF_ZONE_ID 則是你的網域對應的 Zone tag(例如 seer.md 在後台的 ID)。這雖然不是密碼,但放在 environment variables 裡一起管理會比較乾淨,Function 在 runtime 也可以少跑一次 Zone 的查表 request。
另外,你還可以透過這些選填的參數做進一步微調:
CF_ACCOUNT_ID
CF_ZONE_NAME
SITE_METRICS_WINDOW_HOURS=24
SITE_METRICS_CACHE_SECONDS=300
SITE_METRICS_AI_USER_AGENTS=GPTBot,ChatGPT-User,OAI-SearchBot,ClaudeBot,PerplexityBot
SITE_METRICS_SEVEN_DAY_MAX_DAYS=7
這些是額外的選填環境變數,用來微調快取時間、抓取的 AI 爬蟲 User-Agent 清單、以及時間視窗大小。
---
在 Cloudflare Dashboard 上要怎麼設定?
打開 Cloudflare Pages 後台,點擊路徑如下:
Workers & Pages
→ 選擇你的 Pages 專案
→ Settings
→ Variables and Secrets
→ Add
這是 Cloudflare 後台的點擊路徑,照著這個順序就可以把我們剛才定義好的 secrets 和 variables 填進去。
填入時建議的變數屬性如下:
CF_API_TOKEN Secret / Encrypt
CF_ZONE_ID Variable
SITE_METRICS_CACHE_SECONDS Variable
SITE_METRICS_AI_USER_AGENTS Variable
這裡展示了這幾個環境變數的加密型態,像是 API Token 必須設為 Secret 加密,而快取時間等參數設為一般的 Variable 即可。
請記住,設定完環境變數之後需要「重新部署(redeploy)」一次專案才會生效。部署時,這些變數會被掛載到 Pages Function 的 context.env 底下。
---
怎麼實作 Pages Functions 的 API Proxy 轉發?
在 Cloudflare Pages 中,Functions 資料夾下的檔案路徑會自動對應到路由網址。在我的專案中,檔案放在這裡:
functions/api/site-metrics.js
這是 Cloudflare Pages Function 的約定路徑(Convention),只要在這個位置下建檔案,它就會自動被部署成一個 API endpoint。
部署完成後,前端就可以直接打這個網址: https://blog.seer.md/api/site-metrics
我們在 React 元件或前端 Vanilla JS 中,可以這樣非同步讀取資料:
const response = await fetch("/api/site-metrics?range=7d", {
headers: { Accept: "application/json" }
});
const metrics = await response.json();
這段是前端 React 或 Vanilla JS 呼叫同源 API 的寫法,直接用相對路徑 fetch,完全不需要帶任何 API token 或處理跨網域(CORS)問題。
而在 Pages Function 後端,程式碼只做三件事:
- 從
env讀取 Token、Zone ID 以及 Cache 時間設定。 - 組裝 GraphQL 查詢,用
fetch()以 POST 方法打 Cloudflare 的 Analytics 後台。 - 把撈回來的複雜資料整理成簡化版的 JSON,回傳給前端。
Cloudflare GraphQL 的服務端點固定為: https://api.cloudflare.com/client/v4/graphql
轉發時帶上我們隱藏在環境變數的 CF_API_TOKEN:
await fetch("https://api.cloudflare.com/client/v4/graphql", {
method: "POST",
headers: {
Authorization: `Bearer ${env.CF_API_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ query, variables })
});
這段是 Pages Function 後端向 Cloudflare GraphQL 伺服器打 API 的實作,此時 Authorization header 帶的 Bearer Token 是由環境變數注入,前端完全接觸不到。
---
如何寫 GraphQL 查詢 visits 次數?
為了在部落格顯示基本的 Visits 人次,我們需要在 GraphQL 裡撈 httpRequestsAdaptiveGroups:
query SiteMetrics($zoneTag: string, $start: Time, $end: Time) {
viewer {
zones(filter: { zoneTag: $zoneTag }) {
total: httpRequestsAdaptiveGroups(
limit: 1
filter: {
datetime_geq: $start
datetime_lt: $end
requestSource: "eyeball"
edgeResponseStatus_geq: 200
edgeResponseStatus_lt: 400
}
) {
count
sum {
visits
}
}
}
}
}
這是 GraphQL 查詢語法,我們限定 requestSource 為 eyeball(真人訪客而非機器人),且回應碼在 200 到 399 之間,以此來統計真正的 visits 流量。
這裡有兩個統計數字可以拿來參考:
sum.visits:代表不重複訪客的 Visits。count:代表符合篩選條件的總 Request 次數。
---
如何估計 AI 爬蟲(AI Crawler)的流量?
要精準過濾 AI 爬蟲的最直覺方式,是在 GraphQL 查詢中加上 user-agent 的模糊比對:
ai: httpRequestsAdaptiveGroups(
limit: 1
filter: {
datetime_geq: $start
datetime_lt: $end
requestSource: "eyeball"
edgeResponseStatus_geq: 200
edgeResponseStatus_lt: 400
OR: [
{ userAgent_like: "%GPTBot%" }
{ userAgent_like: "%ChatGPT-User%" }
{ userAgent_like: "%OAI-SearchBot%" }
{ userAgent_like: "%ClaudeBot%" }
{ userAgent_like: "%PerplexityBot%" }
]
}
) {
count
}
這是用來篩選特定 AI crawler 的查詢區塊,透過 OR 條件 and userAgent_like 模糊比對,把常見 AI 爬蟲的 request 次數撈出來。
要注意的是,這只能撈出特定 User-Agent 的連線次數,並不能代表這些 AI 真的把你的文章拿去做了什麼用途。但對於一個靜態部落格來說,這種統計方式是最輕量且不依賴外部資料庫的方案。
---
側邊欄展開:各家 AI 爬蟲數據細分
為了讓側邊欄元件點開時可以展示更詳細的 breakdown,我們可以讓 API 回傳各家爬蟲的細分數據:
{
"aiRequests": 47,
"aiCrawlers": [
{ "id": "openai", "name": "OpenAI", "logo": "OpenAI", "requests": 21 },
{ "id": "anthropic", "name": "Claude", "logo": "Claude", "requests": 8 },
{ "id": "perplexity", "name": "Perplexity", "logo": "PPLX", "requests": 6 }
]
}
這是 Pages Function 回傳給前端的 JSON 格式範例,拆分出不同爬蟲的個數,好讓前端元件可以分別渲染出對應的圖示與數據。
目前我們在 Function 裡內建分析了以下幾組常見的 AI 與搜尋引擎爬蟲:
- OpenAI:
GPTBot、ChatGPT-User、OAI-SearchBot - Claude:
ClaudeBot、anthropic-ai - Perplexity:
PerplexityBot - Google:
Google-Extended - Apple:
Applebot-Extended - Common Crawl:
CCBot
---
快取控制(Cache-Control)與時間區間限制
Cloudflare GraphQL Analytics API 有嚴格的 Rate limit。為了避免首頁元件頻繁打 API 導致後台額度被燒光,一定要在 Pages Function 加上快取機制:
Cache-Control: public, max-age=300, stale-while-revalidate=300
這個 HTTP Header 告訴 Cloudflare Edge 快取這份資料 5 分鐘(300 秒),在快取過期時,會先用舊資料渲染,並在背景自動發送 request 去更新快取。
當訪客在首頁切換「24小時」或「7天」區間時,因為有 max-age=300,這些 request 都會直接命中 Edge 快取,大大減輕了後台的負擔。
---
本機開發測試與線上部署驗證
在本地端開發時,我們可以使用 wrangler 來模擬 Pages Functions 的運行環境:
npx wrangler pages dev public
使用 wrangler 來啟動本地端的開發伺服器,它會自動讀取同目錄下的靜態檔案與 Pages Functions 做本地聯調。
記得在專案根目錄下建一個 .dev.vars 檔案,用來存放本地測試用的 Secrets,千萬不要把它 commit 進 Git 中:
CF_API_TOKEN="..."
CF_ZONE_ID="..."
SITE_METRICS_CACHE_SECONDS="300"
這是在本地測試時所建立的環境變數設定檔,讓 wrangler dev 能夠模擬線上的 env context。
部署上線後,我們可以用 curl 來檢驗 API 輸出的 JSON 格式是否正常:
curl https://your-domain/api/site-metrics?range=24h
curl https://your-domain/api/site-metrics?range=7d
這是我們部署完成後,用來測試 API endpoint 是否運作正常、格式是否正確的 curl 指令。
如果回傳格式如下,就代表 API proxy 已經順利接通:
{
"ok": true,
"range": "7d",
"label": "7D",
"visits": 128,
"requests": 932,
"aiRequests": 47,
"aiCrawlers": [
{ "id": "openai", "name": "OpenAI", "logo": "OpenAI", "requests": 21 },
{ "id": "anthropic", "name": "Claude", "logo": "Claude", "requests": 8 },
{ "id": "perplexity", "name": "Perplexity", "logo": "PPLX", "requests": 6 }
],
"source": "cloudflare-graphql",
"aiDetection": "user-agent"
}
這段 JSON 是線上 API 實際吐回的正確資料範例,包含 7 天內(7d)的 visits、requests 與各 AI 爬蟲的抓取統計數據。
如果回傳了 503,請去 Pages 後台確認 CF_API_TOKEN 有沒有填寫正確;如果是 502,則通常是 Zone ID 填錯,或者是 GraphQL 裡設定的時間範圍有問題。
透過這套架構,我們就把靜態網頁(在 Git 版控)與敏感金鑰(留在 Cloudflare 後台)切分得非常乾淨。未來如果需要引入更長期的流量統計,只需要再把 Pages Function 對接 D1 即可,完全不需要去更動原本的前端程式碼架構。
---
相關文件參考
最安全的代理,是只傳遞數據而不留下任何多餘的權限漏洞。
Signals
Visits
--
Waiting for Cloudflare metrics.