# DRF Throttling (verzoeklimiet) volledig beheersen: waarom het nodig is en hoe je het instelt, toepast en aanpast Het gebruik van `limit_req` in een nginx‑reverse proxy is duidelijk effectief. Maar wanneer je **verschillende beleidsregels per view/actie** wilt toepassen (bijv. login 5 keer per minuut, upload 20 keer per dag, view‑API 1000 keer per gebruiker), is het vertrouwen op alleen server‑ of infrastructuur‑instellingen niet ideaal. DRF‑throttling laat je **applicatieniveau‑specifieke beperkingen** maken die passen bij de aard van de endpoint, waardoor het een essentieel basiskennis is. --- ## Waarom throttling nodig is {#sec-c97ae217f027} Throttling beslist of een verzoek wordt toegestaan, net als een permissie, maar het verschil is **permanent (permissie) vs tijdelijk (verzoekfrequentie)**. De DRF‑documentatie beschrijft throttling als een “tijdelijke toestand die de snelheid van verzoeken van een client beheert”. De praktische redenen zijn onder meer: * **Misbruik/aanvallen verminderen**: brute‑force login, spam, crawlen, eenvoudige DoS. * **Kosten/bronbescherming**: uploads, externe API‑aanroepen, zware queries, generatieve AI‑aanroepen. * **Gelijke behandeling (fair use)**: voorkom dat één gebruiker of sleutel de middelen monopoliseert. * **Code‑gebaseerde beleidsregels**: “Deze API mag N keer per minuut” in plaats van alleen via infrastructuur. Belangrijk: throttling kan **globaal** of **per view/actie** worden toegepast, iets waar nginx alleen vaak tekortschiet. --- ## Hoe DRF Throttling werkt (kernconcepten) {#sec-a5c59acab781} ### 1) Rate‑string {#sec-a2424ad14298} DRF gebruikt strings zoals `"100/day"` of `"60/min"`. ### 2) Wie wordt beperkt (clientidentificatie) {#sec-9cf8b4c551fc} * `UserRateThrottle`: voor geauthenticeerde gebruikers op basis van **user id**, voor anonieme op basis van **IP**. * `AnonRateThrottle`: alleen anonieme verzoeken, gebaseerd op **IP**. * `ScopedRateThrottle`: toepast een **scope‑gebaseerd** beleid, bijvoorbeeld `uploads`. IP‑detectie gebruikt `X-Forwarded-For` of `REMOTE_ADDR`; bij een proxy is `NUM_PROXIES` cruciaal. ### 3) Opslag in cache {#sec-7b9c8a453729} De standaardimplementatie slaat de teller op in de Django‑cache. Bij een enkele worker/server is `LocMemCache` voldoende, maar bij meerdere workers of replicas is een gedeelde cache zoals Redis noodzakelijk. --- ## Globale (global) instellingen: snel start {#sec-c94dcae484bb} `settings.py`: ```python REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.AnonRateThrottle", "rest_framework.throttling.UserRateThrottle", ], "DEFAULT_THROTTLE_RATES": { "anon": "100/day", "user": "1000/day", }, } ``` Dit voegt een **basisbeleid** toe aan alle API‑punten. Bij overschrijding geeft DRF standaard een **HTTP 429 (Too Many Requests)** terug. --- ## Throttling per view toepassen: verschillende limieten per endpoint {#sec-491e225aa51d} ### 1) Class‑based view (APIView) {#sec-f32b57a3aba1} ```python 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) Function‑based view (@api_view) {#sec-7a28800ffd4e} ```python 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) Specifieke actie in een ViewSet (@action) {#sec-f2285f7a57f9} ```python 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}) ``` Actie‑specifieke throttling heeft prioriteit boven de ViewSet‑niveau‑instelling. --- ## ScopedRateThrottle gebruiken voor “endpoint‑specifieke” beleid (aanbevolen) {#sec-69d1c4813e63} Scoped throttling maakt het mogelijk om beleid te scheiden op basis van een **betekenisvolle scope** (bijv. `uploads`, `login`). `settings.py`: ```python REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "rest_framework.throttling.ScopedRateThrottle", ], "DEFAULT_THROTTLE_RATES": { "login": "5/min", "uploads": "20/day", "search": "60/min", }, } ``` View‑specifieke scope: ```python 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 maakt een unieke sleutel van **scope + user id of IP**. --- ## Een eigen Throttle maken: de sleutel bepalen is cruciaal {#sec-892da9d3fb29} Hoewel ingebouwde throttles vaak voldoende zijn, komen er in de praktijk vaak de volgende eisen voor: * “Login beperken tot IP + gebruikersnaam” * “Per API‑sleutel beperken” * “Per header/tenant/organisatie beperken” * “Een andere cache (bijv. Redis‑cluster) gebruiken” ### 1) Meest voorkomende aanpak: SimpleRateThrottle overerven {#sec-759164135e01} ```python 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‑gebaseerde identificatie if not username: return None return f"throttle_login:{ident}:{username}" ``` Registratie in settings: ```python REST_FRAMEWORK = { "DEFAULT_THROTTLE_CLASSES": [ "path.to.LoginBurstThrottle", ], "DEFAULT_THROTTLE_RATES": { "login": "5/min", }, } ``` ### 2) Een andere cache gebruiken (cache‑attribuut) {#sec-99ae4b21e2b9} ```python from django.core.cache import caches from rest_framework.throttling import AnonRateThrottle class CustomCacheAnonThrottle(AnonRateThrottle): cache = caches["alternate"] ``` --- ## Voor het uitrollen: belangrijke aandachtspunten {#sec-68bf2bd36ec0} ### 1) IP‑detectie in een proxy‑omgeving {#sec-55e331e40267} Zonder correcte `NUM_PROXIES` kunnen alle gebruikers als één IP worden gezien. ### 2) LocMemCache is zwak bij meerdere workers/servers {#sec-238e19948ccf} Een gedeelde cache zoals Redis is essentieel voor consistente throttling. ### 3) Race‑condition bij hoge concurrentie {#sec-d87294622fb5} De ingebouwde implementatie kan bij hoge concurrentie een paar extra verzoeken toestaan. Voor kritieke scenario’s (bijv. betalingen) overweeg een atomische teller (Redis INCR + EXPIRE). ### 4) Klantvriendelijkheid: 429 en Retry‑After {#sec-f405894aac21} DRF retourneert standaard 429. Door `wait()` te implementeren kun je een `Retry-After`‑header toevoegen. --- ## Conclusie: zowel nginx als DRF‑throttling gebruiken {#sec-b08cdd5cec16} ![Robotclub toegangsbeperking en 429 neonbalk](/media/editor_temp/6/c96bb1d6-7cde-48a0-98aa-07396570391d.png) * **nginx**: blokkeert grote hoeveelheden verkeer of aanvallen direct aan de rand. * **DRF‑throttling**: past verfijnde, endpoint‑specifieke regels toe op applicatieniveau. Voor “per view‑specifieke beperkingen” is DRF‑throttling het meest geschikt en blijft het consistent, zelfs als de serveromgeving verandert.