每天 10 萬次請求、每次 10ms CPU。靜態檔案不計次是最大的槓桿;KV 每天 1000 次寫入會殺掉任何個人化計數器。實際跑一個 100 多頁的站的作法。
發布於 2026-09-06・約 5 分鐘
我有一個站跑在 Cloudflare Workers 上,100 多個網址、33 個工具,全部在免費層裡,沒有付過錢。
免費層的額度是:
請求數 100,000 次 / 天 CPU 時間 10 ms / 次
這兩個數字看起來很緊,實際上很寬鬆——前提是你在設計的時候就知道什麼會計次、什麼不會。
JS、CSS、圖片、字型完全不經過 Worker,也不算請求數。 只有兩種東西會計次:
/psy/
/travel/tax
/api/*
所以一個訪客開一頁、載了 30 個資源,計次是 1,不是 31。
這條的直接推論是:純前端的工具零邊際成本。 我那 33 個工具裡大部分是在瀏覽器裡算完的(心理測驗、匯率換算、日期計算),Worker 只負責把 HTML 吐出去,之後使用者點多久都不會再計次。
設計新功能時第一個問題永遠是「這能不能在瀏覽器裡算完」。能的話就沒有額度問題。
有些資料一定要從別人那裡拿(發票號碼、油價、匯率)。直接讓前端去打上游有兩個問題:CORS,還有你完全不知道自己一天打了幾次。
改成走自己的 Worker 端點,然後一定要掛邊緣快取:
async function proxyWithCache(request, upstream, ttlSeconds) { const cache = caches.default const key = new Request(new URL(upstream).toString(), { method: 'GET' }) let res = await cache.match(key) if (res) return res res = await fetch(upstream) res = new Response(res.body, res) res.headers.set('Cache-Control', `public, max-age=${ttlSeconds}`) res.headers.set('Access-Control-Allow-Origin', '*') await cache.put(key, res.clone()) return res }
TTL 我抓一小時起跳,發票那種一期才更新一次的抓六小時。
效果是上游一天只會被打幾次,不是幾千次。 而且是「每個 colo 幾次」——快取是各個資料中心各自一份,所以實際次數是機房數量乘上更新次數,還是很小。
注意 cache.match 失敗要有退路。邊緣快取不保證命中,它隨時可以把東西丟掉。
cache.match
免費層可以開 KV、Durable Objects、D1,但額度會殺掉你。
KV 每天只有 1000 次寫入。 任何「每個使用者一筆」的東西——瀏覽次數、收藏、投票計數——都會在幾百個訪客之後就寫不進去了。而且寫入失敗通常是安靜的,你會得到一個「數字停住不動」的計數器。
我的作法是需要每人狀態就用 localStorage。使用者的偏好、答過的題目、上次的輸入值,全部存在他自己的瀏覽器裡。缺點是換裝置就沒了,但對一個免費工具站來說這個代價可以接受。
localStorage
真的需要跨裝置同步的功能,那個功能就不該做在這個架構上,該重新設計或直接放棄。這是我覺得最難但最重要的一條:額度限制應該改變你做什麼,而不是只改變你怎麼做。
Cron Triggers 也一樣不用。要排程就放在別的地方跑,不要放在邊緣。
10ms 聽起來很少,但你只要不在裡面做重活就永遠用不完。
我的 Worker 只做兩件事:
一、用 HTMLRewriter 改 meta 標籤。 這個站是 SPA,所有路徑吐的是同一份 HTML。爬蟲和社群平台的預覽抓取器不執行 JS,所以要在邊緣把 <title>、description、OG 標籤依路徑改掉。HTMLRewriter 是串流式的,CPU 成本很低。
<title>
二、/api/* 的 JSON 轉手。 上面那段代理,基本上就是 fetch 加設幾個 header。
fetch
不做的事:不在邊緣產圖、不在邊緣解析大型 XML、不做任何迴圈跑幾千次的東西。
有一個工具要解析電子發票的 XML,我把解析整個放在瀏覽器端做。Worker 只負責把原始檔案轉手過去。這樣 CPU 時間是常數,跟檔案大小無關。
100 多個網址、33 個工具的站,日常用量離 10 萬次差很遠。因為真正計次的只有「有人開一個頁面」這件事,而那是一個很誠實的數字——你有多少訪客就是多少次。
反過來說,如果你的用量逼近上限,通常不是流量真的變大,是有東西在重複打。值得查的兩個地方:前端有沒有在 useEffect 裡沒設依賴陣列導致重複呼叫、以及有沒有爬蟲在掃你的 /api/*。
useEffect
免費層的限制不是「小號的付費層」,它的形狀不一樣。付費層是「用多少付多少」,超了就多繳錢;免費層是超了就停。
所以設計的方向也不同:付費層優化的是單位成本,免費層優化的是讓昂貴的事情根本不發生。靜態檔案不計次、瀏覽器端算完、邊緣快取擋住上游,這三件事都不是在「省」,是在讓計費事件不存在。
一個功能如果只能靠「希望流量不要太大」來留在免費層,那它遲早會出事,而且會在你最不希望的那天出事。
標籤:Cloudflare Workers、免費層、架構、邊緣運算、效能