
stanley
1/20/2026

公司專案使用 Next.js v15 App Router 搭配 Contentful SDK 建立部落格系統。原本預期在全面改為 SSG 之後,Contentful API 用量應該會趨於穩定,但實際觀察卻發現 API 使用量仍持續每日上升,甚至超出 quota。
由於近期並沒有頻繁部署,加上整體流量並未顯著成長,這個現象顯得相當反常。因此開始系統性地檢視專案設定與實際行為,試圖找出 API 用量異常的真正原因。
這次問題的根本原因 並不是 Next.js SSG 設定錯誤,而是前期實作時忽略了一個細節:
Contentful SDK 的 getEntries 單次最多只會回傳 100 筆資料。
隨著文章數量超過 100 筆,只有第一頁的文章能被正確靜態生成,其餘分頁在缺少對應靜態頁面的情況下,實際上都 fallback 成 SSR,導致每次使用者造訪都會重新呼叫 Contentful API,最終造成 API 用量爆炸。
在修正分頁資料取得邏輯後,同時也將所有透過 Contentful SDK 取資料的 function 使用 unstable_cache 包裹,避免在 build time 重複撈取相同資料,實際觀察 Build 階段 API request 約下降 50%。
從 Contentful 後台來看,API 用量呈現「每日穩定增加」而非單日暴衝。搭配 GA 流量比對後,也沒有看到明顯的流量異常,因此初步排除是真實使用者流量造成的問題。
基於這些線索,當時整理出三個可能方向:
Sitemap 設定問題:部分資料為動態生成,並設定 revalidate: 30s,每次更新都會打 Contentful API。
自動化工具影響:專案有使用 Renovate / Dependabot 等自動更新工具,可能導致頻繁 build。
SSG 設定實際未生效:部分頁面可能仍以 SSR 方式運作。
第一點即使假設有人持續刷新 sitemap,一天約 2,880 次 request,與實際觀察到的「每日數萬次 API 呼叫」仍有明顯落差,因此暫時排除。第二點也因近期已關閉自動更新、沒有新的 build 行為而排除,問題逐漸聚焦到 SSG 是否真的全面生效。
透過 yarn build 檢查輸出結果後,確認文章頁面在 build log 中確實標示為 SSG,表面上看起來沒有問題。
由於只有 /posts 相關頁面才會消耗 Contentful API quota,因此將注意力集中在文章清單與分頁邏輯上。我在負責取得文章資料的 function 中加入 console.log,觀察實際呼叫時機,結果發現一個關鍵現象:
這與預期的 SSG 行為並不一致,也讓問題一度卡關。
當時懷疑的方向之一,是不是因為透過 Contentful SDK 取資料的 function 並未被快取,導致重複呼叫 API。相關程式碼如下:
```
export async function getAllPosts() {
console.log('--- getAllPosts called ---')
const res = await contentfulClient.getEntries({
content_type: 'post',
})
return res.items.map((post) => ({
slug: post.fields.slug,
}))
}
```
這個 function 同時被 sitemap 與文章頁使用,只要其中任何一個路徑 fallback 成 SSR,就會放大 API 呼叫次數。後來查資料才確認,Next.js 內建的 fetch cache 並不會自動套用在第三方 SDK 上,因此需要使用 unstable_cache 明確包裹。
實際加上 unstable_cache 後,確實解決了 build time 重複呼叫的問題,但 分頁後段在首次請求時仍然會觸發 API,顯示問題並不只是快取設定。
在比對資料時注意到一個異常現象:
這個「剛好是 100」的數字非常醒目,也讓我立刻聯想到 Contentful SDK 是否有預設回傳上限。實際查詢文件後證實:
getEntries 若未指定 limit 與 pagination,單次最多只會回傳 100 筆資料。
因此在文章數量小於 100 時一切正常;一旦超過 100,後續頁面就不會被納入靜態生成流程,最終全部變成 SSR,API 用量自然開始失控。
這個 bug 之所以拖了一段時間才被發現,關鍵在於過去的專案經驗多半來自 Gatsby。在 Gatsby 中,沒有被生成的頁面會直接回傳 404,因此會很快察覺異常;但在 Next.js App Router 架構下,未被預先生成的頁面會自然 fallback 成 SSR,如果沒有特別留意,很容易被忽略。