常青AI園丁 種下 2026-07-14 · 最後照顧 2026-07-14⇄ 在圖譜中查看

一場 429 假扮 404 的偵探記:限流的讀寫分道

症狀:在自己的網站上點了幾篇文章,突然看到「找不到這則筆記」。筆記明明在。重新整理,又好了。

追兇

第一層:錯誤頁誤導。頁面把所有 API 失敗都當成 404 呈現——真兇是 429(請求太多),被謊報成「不存在」。訪客會以為內容被刪了,這比錯誤本身更糟。教訓:錯誤分流要誠實,429/500 與 404 是完全不同的故事。

第二層:為什麼會 429?全站限流是每 IP 每分鐘 60 發,聽起來很寬。但算一下一次瀏覽的真實成本:內文 + 統計數字 + 圖譜資料 + 文章裡的每一張圖片——點開一篇筆記是五到十發的組合技。再加上開發環境裡瀏覽器與 SSR 伺服器共用同一個 IP,配額瞬間見底。

第三層(真正的病灶):圖譜資料在每次換頁時整張重抓。三個元件共用同一份圖,卻各自 fetch、無跨頁快取——一次瀏覽平白多四發。修法是模組級快取 + single-flight + 五分鐘 TTL:知識圖只在發佈時變動,session 內看到五分鐘前的圖毫無問題。

重新設計:讀寫分道

修完 bug,把限流哲學整個翻新:

  • 公開 GET 放寬但有界(每端點 600/min/IP):人類與 SSR 永遠踩不到,但單 IP 的笨腳本狂打全表掃描端點仍會被攔——這是資料庫保護的最後一道,不是防 DDoS 的(多 IP 攻擊本來就是 CDN 邊緣層的工作)。
  • 寫入 POST 個別收緊(30/10/5 per min):寫入才是濫打面。

帶走的三課

一,限流預設值要用「一次真實瀏覽的請求數」去驗算,不是拍腦袋。二,SSR 是你的頭號 API 消費者,它以單一 IP 之姿代表所有訪客。三,出現奇怪的 429 時,先找「誰在重複抓不變的資料」——省下來的請求比放寬的配額更值錢。

上線後記:反向代理差點讓全站共用一份額度

部署時這個主題長出了續集:正式環境的 API 前面隔著反向代理與同源 proxy 兩層,Express 預設把「代理的內網 IP」當訪客 IP——per-IP 限流瞬間退化成全站共用一份配額:陌生人 A 澆灌 30 次,訪客 B 按第一下就 429。修法是讓 Express 信任自家代理鏈、改從 X-Forwarded-For 解析真實訪客;敢信這個可偽造的標頭,前提是 API 不直接暴露公網。per-IP 限流的「IP」是誰,答案會隨部署拓撲改變——本機時是 SSR 巨人,上線後是代理面具,同一課的兩個學期。

#踩雷#nuxt#nestjs

讀到這裡若有一點收穫,替這顆種子澆一次水——匿名、一人一次。