006

如何在 Cloudflare Pages 上使用流量統計功能 (基於 Cloudflare API )

如何在 Cloudflare Pages 上使用流量統計功能 (基於 Cloudflare API ) 封面圖

靜態部落格也能顯示即時流量與 AI crawler 造訪數。重點是把 Cloudflare token 留在 Pages Function,前端只打同源 API。

Seer

2026-05-28

上個月我在幫部落格加上一個「即時流量統計」與「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 後端,程式碼只做三件事:

  1. env 讀取 Token、Zone ID 以及 Cache 時間設定。
  2. 組裝 GraphQL 查詢,用 fetch() 以 POST 方法打 Cloudflare 的 Analytics 後台。
  3. 把撈回來的複雜資料整理成簡化版的 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 查詢語法,我們限定 requestSourceeyeball(真人訪客而非機器人),且回應碼在 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 與搜尋引擎爬蟲:

  • OpenAIGPTBotChatGPT-UserOAI-SearchBot
  • ClaudeClaudeBotanthropic-ai
  • PerplexityPerplexityBot
  • GoogleGoogle-Extended
  • AppleApplebot-Extended
  • Common CrawlCCBot

---

快取控制(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 即可,完全不需要去更動原本的前端程式碼架構。

---

相關文件參考

最安全的代理,是只傳遞數據而不留下任何多餘的權限漏洞。

Visits

--

Waiting for Cloudflare metrics.