DRF Throttling vollständig verstehen: Warum es nötig ist und wie man es einrichtet, anwendet und anpasst
Ein "limit_req" in einem nginx‑Reverse‑Proxy ist zweifellos effektiv, um den Eingang zu drosseln. Wenn man jedoch unterschiedliche Richtlinien pro View/Action (z. B. Login 5 mal pro Minute, Uploads 20 mal pro Tag, Such‑API 1 000 mal pro Benutzer) setzen möchte, ist es schwierig, sich ausschließlich auf Server‑ oder Infrastruktur‑Einstellungen zu verlassen. DRF‑Throttling ermöglicht es, anwendungsspezifische Beschränkungen zu definieren, die sich an den Eigenschaften der Endpunkte orientieren – ein unverzichtbares Grundwissen.
Warum Throttling nötig ist
DRF‑Throttling entscheidet, ob eine Anfrage erlaubt wird – ähnlich wie ein Permission‑Check – jedoch ist der Unterschied, dass es sich um eine temporäre Beschränkung handelt, nicht um eine dauerhafte Berechtigung. Die Dokumentation beschreibt Throttling als „eine temporäre Zustandskontrolle, die die Geschwindigkeit von API‑Anfragen eines Clients steuert“.
Die praktischen Gründe lassen sich wie folgt zusammenfassen:
- Missbrauch/Angriffe reduzieren: Brute‑Force‑Login, Spam‑Anfragen, Crawling, einfacher DoS
- Kosten/Resourcen schützen: Uploads, externe API‑Aufrufe, schwere Abfragen, generative‑AI‑Calls
- Fairness: Verhindern, dass ein einzelner Benutzer/Key Ressourcen monopolisiert
- Regeln im Code: „Diese API darf pro Minute N‑mal“ – statt auf Infrastruktur‑Ebenen zu setzen
Wichtig: Throttling kann global oder pro View/Action angewendet werden – ein Bereich, in dem nginx allein oft nicht ausreicht.
Wie DRF Throttling funktioniert (nur Kernkonzepte)
1) Rate‑String
DRF verwendet üblicherweise Formate wie "100/day" oder "60/min".
2) Wer wird beschränkt? (Client‑Identifikation)
UserRateThrottle: Authentifizierte Benutzer werden nach user‑id und nicht‑authentifizierte nach IP beschränktAnonRateThrottle: Nur nicht‑authentifizierte Anfragen, basierend auf IPScopedRateThrottle: Richtlinien werden pro Scope (z. B. "uploads") definiert
IP‑Erkennung erfolgt über X-Forwarded-For oder REMOTE_ADDR. Bei Proxy‑Umgebungen ist die Einstellung NUM_PROXIES entscheidend.
3) Zustandsspeicherung – Cache
Die Standard‑Implementierung speichert Zähler im Django‑Cache‑Backend. Bei einer einzelnen Instanz reicht LocMemCache, aber bei mehreren Workern oder Replikaten ist ein gemeinsamer Cache wie Redis fast zwingend.
Globale (Global) Konfiguration: Schnellstart
settings.py:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"anon": "100/day",
"user": "1000/day",
},
}
Damit gilt die Standardrichtlinie für alle Endpunkte. Bei Überschreitung sendet DRF standardmäßig HTTP 429 (Too Many Requests).
Throttling pro View: Unterschiedliche Beschränkungen pro Endpunkt
1) Klassische Views (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})
2) Funktionsbasierte Views (@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‑spezifisch (@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})
Die Action‑Spezifikation hat Vorrang vor der ViewSet‑Einstellung.
ScopedRateThrottle: Richtlinien pro View‑Eigenschaft (empfohlen)
Scoped‑Throttling trennt Richtlinien nach sinnvollen Namen (z. B. "uploads", "login").
settings.py:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"rest_framework.throttling.ScopedRateThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
"uploads": "20/day",
"search": "60/min",
},
}
View‑Deklaration:
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 erstellt einen eindeutigen Schlüssel aus Scope + user‑id oder IP.
Eigene Throttle‑Klasse erstellen: Der Schlüssel ist die Identifikation
Selbst mit eingebauten Throttles gibt es häufig Anforderungen:
- Login nach IP + Username beschränken
- API‑Key‑basiertes Throttling
- Beschränkung nach Header/Tenant/Organisation
- Nutzung eines anderen Caches (z. B. Redis‑Cluster)
1) Häufigste Methode: SimpleRateThrottle erben
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‑basierte Identifikation
if not username:
return None # Kein Username → kein Throttle
return f"throttle_login:{ident}:{username}"
Registrierung in settings.py:
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES": [
"path.to.LoginBurstThrottle",
],
"DEFAULT_THROTTLE_RATES": {
"login": "5/min",
},
}
2) Cache‑Attribute ändern
from django.core.cache import caches
from rest_framework.throttling import AnonRateThrottle
class CustomCacheAnonThrottle(AnonRateThrottle):
cache = caches["alternate"]
Vor dem Deployment: Wichtige Punkte
1) IP‑Erkennung hinter Proxies
X-Forwarded-For/REMOTE_ADDR bestimmen die IP. Ohne korrekte NUM_PROXIES‑Einstellung werden alle Anfragen als ein einziger Client behandelt.
2) LocMemCache bei Multi‑Worker/Server
Lokale Caches führen zu inkonsistenten Zählern. Für stabile Throttles ist ein gemeinsamer Cache wie Redis unerlässlich.
3) Race‑Conditions bei hoher Parallelität
Die Standard‑Implementierung kann bei hohem Durchsatz zu kleinen Überschreitungen führen. Für kritische Fälle (z. B. Zahlungen) empfiehlt sich ein atomarer Zähler (Redis INCR + EXPIRE).
4) Client‑freundlich: 429 & Retry‑After
DRF gibt bei Überschreitung 429 zurück. Durch Implementierung von wait() kann ein Retry-After‑Header gesendet werden.
Fazit: nginx + DRF‑Throttling – beides nutzen

- nginx: Schützt vor massiver Traffic‑Spitzen und Angriffen an der Front‑End‑Schicht
- DRF‑Throttling: Anwendungs‑Level‑Richtlinien, die Endpunkt‑Kosten und -Bedeutung berücksichtigen
Besonders die View‑spezifische Beschränkung ist ein starkes Feature von DRF, das unabhängig von Infrastrukturänderungen im Code bleibt.
Es sind keine Kommentare vorhanden.