速率限制
規則
- 對 API 端點使用 token bucket(權杖桶),對驗證/登入路由使用 sliding window(滑動視窗)
- 優先透過 API 金鑰識別呼叫者,其次才是 IP——對於已驗證的路由,絕不能僅依賴 IP
- 回傳
429 Too Many Requests 狀態碼,並附帶 Retry-After 標頭(秒數)以及 X-RateLimit-Remaining 標頭
- 將計數器儲存在 Redis(或用於無伺服器的 Upstash)中——在多實例部署中絕對不要使用記憶體內儲存
- 設定合理的預設值:一般 API 為 100 次請求/分鐘,驗證路由為 5 次請求/分鐘,讀取操作為 1000 次請求/分鐘
- 對於無伺服器環境,使用
@upstash/ratelimit——它原生處理 Redis 的原子性操作與滑動視窗邏輯
- 以中介軟體的形式實作速率限制——全域套用,並可針對個別路由覆寫為更嚴格的限制
- 對於 Next.js:在 middleware.ts 或 API 路由處理程式中進行速率限制,而非 React 元件中
- 依據層級(免費、專業、企業方案)區分限制——將層級資訊儲存在 JWT 中或透過資料庫查詢取得
- 記錄隨附呼叫者身份的速率限制觸發事件——以便及早偵測濫用模式
模式
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, "10 s"),
analytics: true,
});
const { success, limit, remaining, reset } = await ratelimit.limit(identifier);
應避免事項
- 在無伺服器或多實例設定中使用記憶體內速率限制——狀態會在呼叫之間遺失
- 僅在應用程式層進行速率限制——應同時在 CDN/邊緣層加入限制(如 Cloudflare、Vercel)
- 使用過於嚴格的限制阻擋合法使用者——應從寬鬆的設定開始,再根據資料逐步收緊
- 忘記對 Webhook 和公開表單提交實施速率限制