DRF Throttling(請求限制)全攻略:為何需要、如何設定、如何應用、如何自訂
在 nginx 反向代理中使用 limit_req 來「入口截流」雖然有效,但當你想為不同的視圖/動作設定不同的限制(例如:登入每分鐘 5 次、上傳每天 20 次、查詢 API 每人 1000 次)時,僅靠伺服器或基礎設施的設定往往難以滿足。DRF 的 throttling 允許你在應用層面針對「端點特性」建立限制,這是「必備基礎」之一。
為什麼需要 Throttling
DRF 的 throttling 與「權限(permission)」類似,都是決定是否允許請求,但差異在於:權限是永久性的,而 throttling 是臨時的請求頻率限制。官方文件將 throttling 描述為「控制客戶端對 API 的請求速率的臨時狀態」。
實際需求可歸納為:
- 防止濫用/攻擊:暴力破解登入、垃圾訊息、爬蟲、簡單 DoS 等
- 成本/資源保護:上傳、外部 API 呼叫、重負載查詢、生成式 AI 呼叫等「昂貴」端點
- 公平使用:避免單一使用者/金鑰佔用資源
- 政策編碼化:將「此 API 每分鐘 N 次」等規則以程式碼方式管理,而非僅靠基礎設施
重要的是,throttling 可以全域(global)或針對特定視圖/動作(view/action)設定,這正是 nginx 只能做「入口截流」時的補充。
DRF Throttling 的工作原理(核心概念)
1) Rate 字串
DRF 以類似 "100/day"、"60/min" 的格式設定限制。
2) 「誰」被限制(客戶端識別)
UserRateThrottle:已驗證使用者以 user id 為基準,未驗證則以 IP 為基準AnonRateThrottle:僅對未驗證(anonymous)請求以 IP 為基準ScopedRateThrottle:可為「此視圖是 uploads 範圍」等 scope 單位設定政策
IP 會透過 X-Forwarded-For 或 REMOTE_ADDR 取得,若位於代理後面,NUM_PROXIES 設定尤為重要。
3) 狀態儲存使用 Cache
DRF 的預設 throttling 會將計數存於 Django cache backend。單一進程/單一伺服器可使用 LocMemCache,但多工作者或多副本環境則必須使用 Redis 等共享 Cache。
全域(Global)設定:最快速的起步
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"anon": "100/day",
"user": "1000/day",
},
}
這樣即可為整個 API 應用「基礎政策」。
當請求被限制時,DRF 會回傳 HTTP 429(Too Many Requests)。
如何在視圖中設定:端點別差異化
1) 類別視圖(APIView)
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.throttling import UserRateThrottle
class ExpensiveView(APIView):
throttle_classes = [UserRateThrottle]
def get(self, request):
return Response({"ok": True})
文檔亦建議使用 throttle_classes 於視圖層級設定。
2) 函式視圖(@api_view)
from rest_framework.decorators import api_view, throttle_classes
from rest_framework.throttling import UserRateThrottle
from rest_framework.response import Response
@api_view(["GET"])
@throttle_classes([UserRateThrottle])
def ping(request):
return Response({"pong": True})
3) ViewSet 的特定 action(@action)
from rest_framework.decorators import action
from rest_framework.throttling import UserRateThrottle
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
class ItemViewSet(ViewSet):
@action(detail=True, methods=["post"], throttle_classes=[UserRateThrottle])
def purchase(self, request, pk=None):
return Response({"purchased": pk})
action 內設定的 throttling 會覆蓋 ViewSet 層級的設定。
使用 ScopedRateThrottle 建立「視圖特性」政策(強烈推薦)
Scoped throttling 允許你以「有意義的名稱(scope)」區分政策,例如:uploads、login,使運營更清晰。
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.ScopedRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
"uploads": "20/day",
"search": "60/min",
},
}
在視圖中只需宣告 throttle_scope:
from rest_framework.views import APIView
from rest_framework.response import Response
class LoginView(APIView):
throttle_scope = "login"
def post(self, request):
return Response({"ok": True})
DRF 會為帶有 throttle_scope 的視圖使用 ScopedRateThrottle,並以「scope + 使用者 id 或 IP」作為唯一鍵計數。
自訂 Throttle:關鍵在「如何抓取鍵」
雖然內建 throttling 已能解決大部分需求,但實務上常見的需求包括:
- 「登入以 IP + 使用者名稱組合限制」
- 「以 API Key 限制」
- 「以特定標頭/租戶/組織單位限制」
- 「使用非預設 Cache(如 Redis cluster)」
1) 最常見方式:繼承 SimpleRateThrottle
只要正確實作 get_cache_key(),即可自由決定「以什麼為基準」限制。
from rest_framework.throttling import SimpleRateThrottle
class LoginBurstThrottle(SimpleRateThrottle):
scope = "login"
def get_cache_key(self, request, view):
username = (request.data.get("username") or "").lower().strip()
ident = self.get_ident(request) # IP 基礎識別(受代理設定影響)
if not username:
return None # 若無使用者名稱則不限制(可自行調整)
return f"throttle_login:{ident}:{username}"
並於 settings 註冊:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"path.to.LoginBurstThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
},
}
2) 想改用其他 Cache(cache attribute)
from django.core.cache import caches
from rest_framework.throttling import AnonRateThrottle
class CustomCacheAnonThrottle(AnonRateThrottle):
cache = caches["alternate"]
部署前必知!
1) 代理環境下 IP 取得問題
IP 透過 X-Forwarded-For/REMOTE_ADDR 取得,若代理後未正確設定 NUM_PROXIES,所有使用者可能被視為同一 IP。
2) LocMemCache 在多工作者/多伺服器環境下脆弱
本地 Cache 會在每個工作者/伺服器間獨立計數,導致 throttling 無法正確工作。建議使用 Redis 等共享 Cache。
3) 同步競爭條件(race condition)
內建 throttling 在高併發下可能允許「多於 N 次」請求。若需「精確 N 次」的關鍵操作(如支付、優惠券),建議使用 Redis INCR + EXPIRE 等原子操作自行實作。
4) 客戶端友好:429 與 Retry-After
DRF 會回傳 429,若實作 wait() 方法,可在回應中加入 Retry-After 標頭,告知客戶何時可再次嘗試。
總結:nginx 與 DRF throttling,兩者皆可用

- nginx:在大量流量/攻擊面前,第一道防線,快速截斷
- DRF throttling:在應用層面,根據端點意義與成本,精細化限制
特別是「視圖別差異化」的限制,DRF throttling 既易於實作,又能在伺服器環境變更時保持程式碼一致,極具彈性。