{"id":21255,"date":"2026-09-02T08:35:33","date_gmt":"2026-09-02T06:35:33","guid":{"rendered":"https:\/\/webhosting.de\/nginx-rate-limiting-schutz-vor-bot-traffic-angriffen-secure\/"},"modified":"2026-09-02T08:35:33","modified_gmt":"2026-09-02T06:35:33","slug":"nginx-snelheidsbeperking-bescherming-tegen-aanvallen-door-botverkeer-beveiliging","status":"publish","type":"post","link":"https:\/\/webhosting.de\/nl\/nginx-rate-limiting-schutz-vor-bot-traffic-angriffen-secure\/","title":{"rendered":"NGINX Rate Limiting: effectieve bescherming tegen botverkeer en aanvallen"},"content":{"rendered":"<p><strong>NGINX-snelheid<\/strong> Limiting stopt geautomatiseerde verzoeken, beperkt piekbelasting en beschermt login-, API- en formulier-eindpunten tegen bots en aanvallen. Ik laat je zien hoe je limieten instelt en deze toepast voor <strong>Bescherming tegen bots<\/strong> toepast en daaruit een robuust beveiligingsconcept voor drukbezochte websites ontwikkelt.<\/p>\n\n<h2>Centrale punten<\/h2>\n\n<p><strong>Essentieel<\/strong> Dit zijn de belangrijkste punten:<\/p>\n<ul>\n  <li><strong>Tariefgrenzen<\/strong> remmen kwaadaardige aanvallen af en beschermen backend-bronnen.<\/li>\n  <li><strong>Burst\/zonder vertraging<\/strong> vangen legitieme pieken op zonder gebruikers te blokkeren.<\/li>\n  <li><strong>Zones<\/strong> Maak een onderscheid tussen mensen en bots met verschillende limieten.<\/li>\n  <li><strong>Loggen<\/strong> levert gegevens om de grenzen iteratief te verfijnen.<\/li>\n  <li><strong>Integratie<\/strong> met WAF, DDoS-bescherming en monitoring wordt het effect vergroot.<\/li>\n<\/ul>\n\n<h2>Waarom rate limiting aanvallen in een vroeg stadium stopt<\/h2>\n\n<p>Aanvallers zetten in op hoge <strong>Verzoekfrequenties<\/strong>, om inlogformulieren te misbruiken, API\u2019s te overbelasten of inhoud automatisch te scrapen. Daarom beperk ik het aantal verzoeken per sleutel \u2013 meestal per IP-adres \u2013 en bepaal ik of ik ze afrem, vertraag of met een 429-statuscode beantwoord. Zo houd ik botverkeer weg van de CPU, de database en de applicatielogica en laat ik legitieme gebruikers door. Vooral gevoelige paden zoals \/login, \/auth, \/xmlrpc.php of zoekopdrachten die veel resources vergen, profiteren hier sterk van. De bron voor deze methode is de <strong>Nginx-documentatie<\/strong> naar de ngx_http_limit_req_module.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img fetchpriority=\"high\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/09\/nginx-rate-limiting-rechenzentrum-4738.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Hoe de NGINX-module in de praktijk werkt<\/h2>\n\n<p>De module werkt volgens het <strong>Lekke emmer<\/strong>-Principe: Voor elke sleutel slaat NGINX tellerstanden op in een zone en vergelijkt deze met de toegestane frequentie. Typische sleutels zijn $binary_remote_addr voor IP-adressen, tokens voor API-sleutels of afgeleide waarden via map. Als een client de snelheid en de burst-buffer blijvend overschrijdt, wijst NGINX het verzoek af voordat het de backend bereikt. Dit bespaart rekenkracht en vermindert de latentie voor echte bezoekers. Ik stel de reactie in op 429 Too Many Requests of, naar keuze, een andere <strong>Statuscode<\/strong> eh.<\/p>\n\n<h2>Configuratie: stap voor stap uitgelegd<\/h2>\n\n<p>Ik begin met een zone in de http-sectie, stel een gematigde snelheid in en activeer deze gericht op gevoelige paden. Voor kortstondige pieken definieer ik een burst, eventueel met nodelay, om abrupte weigeringen te voorkomen. Vervolgens test ik dit in de staging-omgeving en analyseer ik de logbestanden voordat ik het in productie neem. Zo loop ik geen risico op onnodige blokkades voor echte gebruikers. Een beknopt voorbeeld illustreert de <strong>Syntaxis<\/strong> tastbaar:<\/p>\n\n<pre><code># http {}\nlimit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=10r\/s;\n\nserver {\n  location \/api\/ {\n    limit_req zone=req_limit_per_ip burst=20 nodelay;\n    limit_req_status 429;\n  }\n\n  location \/login {\n    limit_req zone=req_limit_per_ip burst=5;\n    limit_req_status 429;\n  }\n}\n<\/code><\/pre>\n\n<h2>Botbescherming met zones en user-agent-logica<\/h2>\n\n<p>IP-gebaseerde limieten zijn zelden voldoende tegen gedistribueerde botnetten, daarom verdeel ik het verkeer in <strong>Zones<\/strong>: Mensen krijgen ruimere limieten, generieke crawlers strengere. Met map evalueer ik user-agents, herken ik vrijgestelde bots zoals Googlebot en stel ik voor hen eigen, streng gecontroleerde limieten in. Voor onbekende scrapers stel ik strenge limieten in op kostbare paden. Als er patronen opvallen, verhoog ik de strengheid dynamisch, totdat de <strong>Prijs<\/strong> weer binnen de normale waarden ligt.<\/p>\n\n<h2>Fijnafstemming: snelheid, burst, nodelay en statuscodes<\/h2>\n\n<p>De rate regelt de doorvoer per seconde, de burst maakt kortstondige buffering mogelijk en nodelay bepaalt of ik de voorkeur geef aan buffering of onmiddellijke doorlaat. Ik begin gematigd, bijvoorbeeld 10r\/s met burst 20 op API's, en pas dit aan na analyse van de logbestanden. Voor inlogroutes stel ik bijvoorbeeld 1r\/s in met een kleine burst om brute-force-aanvallen af te remmen. Bij overschrijdingen retourneer ik een 429-statuscode, omdat clients daarmee <strong>het hoofd boven water houden<\/strong> en de retry-logica goed werkt. In bijzondere gevallen gebruik ik alternatieve codes als klanten daar om vragen.<\/p>\n\n<h2>Overzicht in de tabel: richtlijnen en toepassing<\/h2>\n\n<p>De volgende <strong>Tabel<\/strong> vat de belangrijkste richtlijnen samen en laat zien wanneer deze zinvol zijn.<\/p>\n\n<table>\n  <thead>\n    <tr>\n      <th>richtlijn<\/th>\n      <th>Effect<\/th>\n      <th>Voorbeeld<\/th>\n      <th>Typisch gebruik<\/th>\n    <\/tr>\n  <\/thead>\n  <tbody>\n    <tr>\n      <td>limit_req_zone<\/td>\n      <td>Geef de sleutel, zone en <strong>Prijs<\/strong> vast<\/td>\n      <td>limit_req_zone $binary_remote_addr zone=perip:10m rate=10r\/s;<\/td>\n      <td>Basis per IP, token of user-agent<\/td>\n    <\/tr>\n    <tr>\n      <td>limit_req<\/td>\n      <td>Activeert de limiet in <strong>Locatie<\/strong>\/Server<\/td>\n      <td>limit_req zone=perip burst=20 nodelay;<\/td>\n      <td>Fijnafstemming per pad of vHost<\/td>\n    <\/tr>\n    <tr>\n      <td>limit_req_status<\/td>\n      <td>Voegt HTTP-code toe <strong>overschrijding<\/strong><\/td>\n      <td>limit_req_status 429;<\/td>\n      <td>Correcte client-werking en herhalingspogingen<\/td>\n    <\/tr>\n    <tr>\n      <td>kaart<\/td>\n      <td>Leidt verzoeken door naar <strong>Zones<\/strong> op<\/td>\n      <td>map $http_user_agent $is_bot {\u2026}<\/td>\n      <td>Onderscheid tussen bot en mens op basis van user-agent<\/td>\n    <\/tr>\n  <\/tbody>\n<\/table>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/09\/NGINXLimitingMeeting1234.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Praktijk: het inlogpunt gericht beveiligen<\/h2>\n\n<p>Ik pas zeer strenge beperkingen toe op \/login, omdat bots wachtwoorden met een hoge <strong>Frequentie<\/strong> uitproberen. 1 aanvraag per seconde met burst 3 voorkomt massaal gissen, zonder echte gebruikers te hard te treffen. Daarnaast registreer ik herhaalde mislukkingen in het logboek om IP-adressen tijdelijk te blokkeren. In combinatie met 2FA en optioneel Captcha neemt de druk op de database en de sessiebeheer aanzienlijk af. Zo houd ik het aantal mislukte pogingen laag en zorg ik ervoor dat de <strong>Toegang<\/strong> stabiel en klaar.<\/p>\n\n<h2>Praktijk: API\u2019s op een eerlijke en gecontroleerde manier beschikbaar stellen<\/h2>\n\n<p>API's hebben duidelijke <strong>Kansen<\/strong>, zodat individuele clients niet de volledige bandbreedte in beslag nemen. Voor algemene routes stel ik 10r\/s en burst 20 in, voor kostbare eindpunten strengere waarden. Als er tokens of API-sleutels beschikbaar zijn, beperk ik per token in plaats van per IP-adres. Dat zorgt voor eerlijkheid tussen klanten en voorkomt misbruik. Een uitgebreidere uitleg vind je in mijn opmerking over <a href=\"https:\/\/webhosting.de\/nl\/api-rate-limiting-hosting-bescherming-tegen-misbruik-beveiliging\/\">API-snelheidsbeperking<\/a>, waarin het concept in een bredere context wordt geplaatst.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/09\/nginx-rate-limiting-security-5721.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Monitoring, logboekregistratie en iteratieve verfijning<\/h2>\n\n<p>Ik log 429-antwoorden, inclusief <strong>Sleutel<\/strong> (bijv. IP of token) en het pad, om patronen te herkennen. Pieken op een klein aantal paden duiden op scraping of brute-force-aanvallen; verspreide druk wijst op botnetten. Met deze gegevens stel ik alleen limieten in waar dat nodig is en minimaliseer ik valse positieven. Dashboards met snelheden, foutpercentages en latentie laten me het effect van elke wijziging zien. Zo blijft de <strong>Prestaties<\/strong> hoog, terwijl de bescherming toeneemt.<\/p>\n\n<h2>Integratie in een holistisch beveiligingsconcept<\/h2>\n\n<p>Ik beschouw rate limiting als een sterke eerste stap <strong>laag<\/strong>, maar ik combineer dit met WAF-regels, IP-reputatie en TLS-beveiliging. Tegen volumineuze aanvallen helpt een DDoS-bescherming stroomopwaarts, die het verkeer op netwerkniveau filtert voordat NGINX in actie hoeft te komen. Ik meet continu statistieken, stel waarschuwingen in voor ongebruikelijke pieken en reageer met regelupdates. Zo ontstaat uit verschillende bouwstenen een robuust beschermingsnetwerk. Deze bieden een praktijkgericht overzicht <a href=\"https:\/\/webhosting.de\/nl\/ddos-beperking-webhosting-strategieen-bescherming-netwerk\/\">DDoS-strategie\u00ebn<\/a>.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/09\/nginx_schutz_tech_office_6352.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Concrete configuratiepatronen voor bots versus mensen<\/h2>\n\n<p>Ik verdeel bezoekers met map in categorie\u00ebn en leid ze naar aparte <strong>Zones<\/strong>. Bekende crawlers krijgen gematigde limieten, generieke agents strengere. Voor paden zoals \/search of \/report hanteer ik strengere regels, omdat deze veel CPU-capaciteit in beslag nemen. Bij herhaalde overtredingen verhoog ik de limieten niet, maar blokkeer ik de toegang tijdelijk of verplaats ik de controle naar een botdetectiemodule. Zo blijft de <strong>Percentage misbruik<\/strong> laag, zonder zoekmachines te verstoren.<\/p>\n\n<h2>Voorbeeld: twee zones en user-agent-toewijzing<\/h2>\n\n<p>Het volgende fragment laat de indeling zien op basis van <strong>Gebruiker agent<\/strong> en het toekennen van passende limieten. Ik combineer dit met gedifferentieerde statuscodes en logboekvelden om het effect nauwkeurig te meten. Bots met een generieke agent komen in de strenge zone terecht. Mensen of geverifieerde crawlers maken gebruik van de soepelere zone. Deze aanpak levert voorspelbare <strong>Doorvoer<\/strong> per klas:<\/p>\n\n<pre><code>map $http_user_agent $is_bot {\n  default 0;\n  \"~*googlebot\"     0;\n  \"~*bingbot\" 0;\n  \"~*crawler|scraper|bot\" 1;\n}\n\nlimit_req_zone $binary_remote_addr zone=human:10m rate=10r\/s;\nlimit_req_zone $binary_remote_addr zone=bot:10m   rate=1r\/s;\n\nserver {\n  location \/ {\n    if ($is_bot) {\n limit_req zone=bot burst=5;\n    }\n    if ($is_bot = 0) {\n limit_req zone=human burst=20 nodelay;\n    }\n    limit_req_status 429;\n  }\n}\n<\/code><\/pre>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/09\/nginx_rate_limiting_schutz_3947.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Foutafhandeling: 429 correct communiceren<\/h2>\n\n<p>Bij Limits geef ik een duidelijk <strong>Antwoord<\/strong> met een indicatie wanneer het zinvol is om het opnieuw te proberen. Voor API\u2019s hoort de geldige Retry-After-header hierbij, zodat clients \u2018backoff\u2019 toepassen. Menselijke gebruikers krijgen een korte uitleg zonder technische details. Dit vermindert het aantal tickets en zorgt voor begrijpelijk gedrag. Een overzichtelijke <strong>UX<\/strong> maakt beperkingen acceptabel en voorkomt frustratie.<\/p>\n\n<h2>Host, netwerk en kernel: de basis versterken<\/h2>\n\n<p>Veel legitiem verkeer en beveiligingsmaatregelen vereisen betrouwbare <strong>Bronnen<\/strong> en zinvolle standaardinstellingen op netwerkniveau. Ik let op de nieuwste NGINX-versies, voldoende RAM voor zones en beveiligingsfuncties tegen transportaanvallen. Tegen SYN-floods helpt het om <a href=\"https:\/\/webhosting.de\/nl\/tcp-syn-cookies-bescherming-tegen-syn-flood-kernel\/\">TCP SYN-cookies<\/a> in de kernel, zodat verbindingen niet vastlopen. Al met al ontlast dit NGINX van onnodige belasting. Zo richt ik de limieten op de HTTP-lagen en houd ik de <strong>Doorvoer<\/strong> stabiel.<\/p>\n\n\n<figure class=\"wp-block-image size-full is-resized\">\n  <img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/webhosting.de\/wp-content\/uploads\/2026\/09\/nginx-ratelimit-schutz-4829.png\" alt=\"\" width=\"1536\" height=\"1024\"\/>\n<\/figure>\n\n\n<h2>Kort samengevat: zo pas ik NGINX-rate limiting effectief toe<\/h2>\n\n<p>Ik beperk het aantal aanvragen per <strong>toets<\/strong>, scherm kritieke paden af en houd bots op afstand met strenge zones. Burst en nodelay helpen om legitieme pieken toe te laten zonder misbruik in de hand te werken. Aan de hand van 429-logs kalibreer ik de waarden voortdurend en verscherp ik de limieten alleen waar dat nodig is. In combinatie met WAF, DDoS-bescherming, monitoring en kernelhardening ontstaat een robuust beveiligingsconcept. Wie dit consequent implementeert, vermindert het botverkeer aanzienlijk en behoudt <strong>Prestaties<\/strong> zelfs onder belasting.<\/p>\n\n<h2>Bouwstenen die in de praktijk vaak ontbreken<\/h2>\n\n<p>In veel opstellingen ontbreken enkele cruciale componenten die het effect van rate limiting merkbaar vergroten:<\/p>\n<ul>\n  <li><strong>Echte client-IP-adressen achter proxyservers<\/strong>: Zonder correcte afhandeling van het echte IP-adres beperkt NGINX vaak het IP-adres van de load balancer \u2013 de limieten gelden dan voor alle gebruikers die daarachter zijn gebundeld.<\/li>\n  <li><strong>Proefdraaien (dry-run)<\/strong>: Limieten worden \u201eblind\u201c geactiveerd. Het is beter om van tevoren alleen bij te houden hoe vaak een limiet zou zijn geactiveerd.<\/li>\n  <li><strong>Sleutels met fijne korrelgrootte<\/strong>: In plaats van alleen op IP-adres te beperken, is het de moeite waard om per API-token, sessie of gebruiker limieten in te stellen om de eerlijkheid te vergroten.<\/li>\n  <li><strong>Samenwerking met limit_conn<\/strong>: Parallelle verbindingen en verzoekfrequenties dekken verschillende misbruikpatronen af.<\/li>\n  <li><strong>Gerichte uitzonderingen<\/strong>: Health-checks, webhooks of interne diensten hebben vaak minder strenge limieten nodig of helemaal geen limieten.<\/li>\n<\/ul>\n\n<h2>Reverse proxy: het echte IP-adres van de client veilig analyseren<\/h2>\n\n<p>Als NGINX achter een load balancer staat, stel ik de Real-IP-richtlijnen in, zodat $binary_remote_addr de echte client weergeeft. Ik vertrouw alleen netwerken die van mij zijn en schakel recursieve evaluatie in:<\/p>\n\n<pre><code>http {\n  # Betrouwbare proxy-IP-bereiken (voorbeeld)\n  set_real_ip_from 10.0.0.0\/8;\n  set_real_ip_from 192.168.0.0\/16;\n  # eventueel openbare LB-\/CDN-bereiken toevoegen\n\n  real_ip_header X-Forwarded-For;\n  real_ip_recursive on;\n\n  limit_req_zone $binary_remote_addr zone=perip:20m rate=10r\/s;\n}\n<\/code><\/pre>\n\n<p>Zonder deze instelling treft een limiet anders onschuldig veel gebruikers tegelijk. Na de installatie controleer ik aan de hand van de toegangslogs of het verwachte IP-adres van de client verschijnt.<\/p>\n\n<h2>Kernstrategie: IP, gebruikers, tokens en pad<\/h2>\n\n<p>De gekozen sleutel is bepalend voor de eerlijkheid en de effectiviteit. Enkele beproefde voorbeelden:<\/p>\n<ul>\n  <li><strong>Pro IP<\/strong> ($binary_remote_addr): Snel inzetbaar, geschikt voor \/login en anonieme eindpunten.<\/li>\n  <li><strong>Per API-token<\/strong>: Eerlijkheid tussen klanten; biedt bescherming tegen NAT-bundeling. Ik haal tokens op via map.<\/li>\n  <li><strong>Per padklasse<\/strong>: Stel voor dure eindpunten afzonderlijke limieten in, bijvoorbeeld \/search strenger dan \/status.<\/li>\n<\/ul>\n\n<pre><code>map $http_authorization $api_token {\n  default \"\";\n  \"~*^Bearer\\s+(.+)$\" $1;\n}\n\nlimit_req_zone $api_token zone=per_token:30m rate=5r\/s;\n\nserver {\n  location \/api\/ {\n    # Alleen van belang als er een token aanwezig is\n    limit_req zone=per_token burst=10;\n    limit_req_status 429;\n  }\n}\n<\/code><\/pre>\n\n<p>Belangrijk: een hoge sleutelcardinaliteit neemt veel geheugen in de zone in beslag. Zorg voor voldoende bufferruimte en houd het geheugengebruik in de gaten.<\/p>\n\n<h2>Opslag en dimensionering van de zones<\/h2>\n\n<p>De zone slaat per actieve sleutel metadata op. Het verbruik bedraagt per vermelding enkele tientallen bytes plus overhead. Daaruit leid ik af:<\/p>\n<ul>\n  <li>Voor veel gelijktijdige IP-adressen\/tokens kies ik grotere zones, bijvoorbeeld 50\u2013100 MB.<\/li>\n  <li>Ik begin vrij ruim en bekijk de NGINX-logs: \u201eshared memory zone is full\u201c duidt erop dat er opnieuw moet worden aangepast.<\/li>\n  <li>Ongebruikte sleutels vervallen na een korte periode van inactiviteit; pieken zijn belangrijker dan het daggemiddelde.<\/li>\n<\/ul>\n\n<h2>Burst en nodelay nauwkeurig toepassen<\/h2>\n\n<p>Zonder <strong>nodelay<\/strong> NGINX rangschikt overschrijdingen binnen de burst-buffer en <em>vertraagd<\/em> Verzoeken. Met <strong>nodelay<\/strong> Toegestane burst-verzoeken worden onmiddellijk doorgelaten, overtollige verzoeken worden afgewezen. Mijn aanpak:<\/p>\n<ul>\n  <li><strong>Interactieve routes<\/strong> (HTML): liever zonder `nodelay`, om korte wachttijden te cre\u00ebren in plaats van een harde 429.<\/li>\n  <li><strong>API's<\/strong>: vaak met `nodelay`, zodat clients duidelijk een 429-status krijgen en een backoff toepassen.<\/li>\n  <li><strong>Dure eindpunten<\/strong>: een korte burst om pieken in de backend af te vlakken.<\/li>\n<\/ul>\n\n<h2>Proefdraaien, logniveau en evaluatie<\/h2>\n\n<p>Voordat ik limieten activeer, schakel ik \u2018Dry-Run\u2019 in en pas ik het logniveau aan. Zo zie ik het effect zonder risico:<\/p>\n\n<pre><code>server {\n  location \/api\/ {\n    limit_req zone=perip burst=20;\n    limit_req_dry_run on; # alleen loggen, niet blokkeren\n    limit_req_log_level notice;  # minder streng dan 'error'\n  }\n}\n<\/code><\/pre>\n\n<p>Vervolgens analyseer ik de toegangsgegevens van de afgelopen 3\u20137 dagen, breng ik hotspots in kaart, pas ik de rate\/burst aan en schakel ik pas daarna de dry-run uit.<\/p>\n\n<h2>429 op de juiste manier doorgeven: HTML, JSON en Retry-After<\/h2>\n\n<p>Voor een goede gebruikerservaring maak ik onderscheid tussen browsers en API-clients en gebruik ik <strong>Retry-After<\/strong>. Zo breng ik grenzen duidelijk over:<\/p>\n\n<pre><code>map $http_accept $wants_json {\n  default 0;\n  \"~*application\/json|\/json\"    1;\n}\n\nserver {\n  error_page 429 = @rate_limited;\n\n  location @rate_limited {\n    add_header Retry-After 2 always;\n    if ($wants_json) {\n add_header Content-Type application\/json;\n      return 429 '{\"error\":\"too_many_requests\",\"retry_after\":2}';\n    }\n    return 429 \"Probeer het later nog eens.\";\n  }\n}\n<\/code><\/pre>\n\n<p>API's kunnen op deze manier programmatisch reageren; gebruikers krijgen een duidelijke melding te zien.<\/p>\n\n<h2>limit_req en limit_conn combineren<\/h2>\n\n<p><strong>limit_req<\/strong> gerichte doorvoer per tijdsvak, <strong>limit_conn<\/strong> beperkt het aantal gelijktijdige verbindingen. Tegen downloads, chatty-clients of HTTP\/2-overbelasting gebruik ik een combinatie van beide:<\/p>\n\n<pre><code>limit_conn_zone $binary_remote_addr zone=perip_conn:10m;\n\nserver {\n  location \/api\/ {\n    limit_req  zone=perip burst=20 nodelay;\n    limit_conn zone=perip_conn 20;  # max. 20 gelijktijdige verbindingen per IP\n  }\n}\n<\/code><\/pre>\n\n<p>Zo voorkom ik dat een klein aantal clients zich weliswaar aan de limiet houdt, maar door te veel gelijktijdige verbindingen te veel resources in beslag neemt.<\/p>\n\n<h2>Uitzonderingen, gezondheidscontroles en interne routes<\/h2>\n\n<p>Niet elk pad heeft limieten nodig. Health-checks (\/healthz), interne webhooks of betalingscallbacks krijgen hun eigen locaties zonder `limit_req` \u2013 of met lagere waarden:<\/p>\n\n<pre><code>server {\n  # geen limieten voor health-checks\n  location = \/healthz { return 200 \"ok\"; }\n\n  # zachte limieten voor betalings-callbacks\n  location \/webhooks\/pay\/ {\n    limit_req zone=perip burst=5;\n  }\n\n  # strikte beveiliging voor inloggen\n  location = \/login {\n    limit_req zone=perip rate=1r\/s burst=3;\n  }\n}\n<\/code><\/pre>\n\n<p>Gedetailleerde uitzonderingen verminderen het aantal valse positieven en zorgen voor stabiele integraties.<\/p>\n\n<h2>Robuustere zonale routing zonder 'if'-trucs<\/h2>\n\n<p>Voor de scheiding tussen \u201ebot vs. mens\u201c geef ik de voorkeur aan interne omleidingen via named locations. Dat maakt de configuratie duidelijk en voorspelbaar:<\/p>\n\n<pre><code>map $http_user_agent $is_bot {\n  default 0;\n  \"~*googlebot|bingbot\" 0;\n  \"~*crawler|scraper|bot\" 1;\n}\n\nlimit_req_zone $binary_remote_addr zone=human:20m rate=10r\/s;\nlimit_req_zone $binary_remote_addr zone=bot:10m   rate=1r\/s;\n\nserver {\n  error_page 418 = @bot;\n\n  location \/ {\n    if ($is_bot) { return 418; }   # interne omleiding\n    limit_req zone=human burst=20 nodelay;\n    limit_req_status 429;\n    try_files $uri $uri\/ \/index.html;\n  }\n\n  location @bot {\n    limit_req zone=bot burst=5;\n    limit_req_status 429;\n  }\n}\n<\/code><\/pre>\n\n<p>Zo komen bots op deterministische wijze in de strenge zone terecht, en mensen in de ontspannen zone \u2013 zonder dat beide grenzen tegelijkertijd van kracht zijn.<\/p>\n\n<h2>Testen, meten, aantrekken: een pragmatische werkwijze<\/h2>\n\n<ul>\n  <li><strong>Staging<\/strong>: Kies een conservatieve instelling voor Rate\/Burst, activeer Dry-Run en test de synthetische belasting tegen het hot-pad.<\/li>\n  <li><strong>Rooktesten<\/strong>: Gebruik curl of Lasttools om korte bursts te genereren en het 429\/Delay-gedrag te controleren.<\/li>\n  <li><strong>Productiviteitsproefproject<\/strong>: Pas het eerst toe op afzonderlijke locaties en houd de logbestanden nauwlettend in de gaten.<\/li>\n  <li><strong>Iteratief scherpen<\/strong>: Pas alleen daar strengere limieten toe waar patronen opvallen; minimaliseer valse alarmen.<\/li>\n<\/ul>\n\n<pre><code># Voorbeeld: snelle burst-test met curl\nfor i in {1..50}; do curl -s -o \/dev\/null -w \"%{http_code}\\n\" https:\/\/example.com\/login &amp; done; wait\n<\/code><\/pre>\n\n<h2>Minuten- in plaats van seconden-tarieven en gedetailleerde routes<\/h2>\n\n<p>NGINX ondersteunt snelheden in seconden of minuten (<strong>r\/s<\/strong>, <strong>r\/m<\/strong>). Bij misbruik van de inlogfunctie stel ik vaak 60r\/m in plaats van 1r\/s in, om korte, legitieme dubbelklikken toe te staan, maar continu klikken te beperken. Dure paden krijgen strengere limieten dan goedkope. Voorbeeld:<\/p>\n\n<pre><code>limit_req_zone $binary_remote_addr zone=perip_min:20m rate=60r\/m;\n\nserver {\n  location \/search\/ {\n    limit_req zone=perip_min burst=10;   # strenger\n  }\n  location \/status {\n    # geen limiet \u2013 goedkoop en voor intern gebruik\n    return 200;\n  }\n}\n<\/code><\/pre>\n\n<h2>Valkuilen en hoe ik ze vermijd<\/h2>\n\n<ul>\n  <li><strong>Verkeerde sleutel<\/strong>: Achter proxyservers zonder echt IP-adres leg ik per ongeluk aan alle gebruikers tegelijk een beperking op.<\/li>\n  <li><strong>Te kleine zones<\/strong>: \u201ezone is full\u201c leidt tot onvoorspelbaar gedrag \u2013 zorg voor ruime afmetingen.<\/li>\n  <li><strong>Een limiet voor alles<\/strong>: Verschillende trajecten vereisen verschillende waarden; een uniforme aanpak leidt tot frustratie.<\/li>\n  <li><strong>Geen bewaking<\/strong>: Zonder 429-analyse blijven verkeerde configuraties onopgemerkt.<\/li>\n  <li><strong>Over de whitelist<\/strong>: Te ruime uitzonderingen zetten de deur wijd open \u2013 zorg voor een gerichte, tijdelijke en transparante whitelist.<\/li>\n<\/ul>\n\n<h2>Bijzonderheden met HTTP\/2, SSE en caching<\/h2>\n\n<p>HTTP\/2 bundelt verzoeken via een klein aantal verbindingen; <strong>limit_conn<\/strong> blijft toch relevant, want streams verbruiken resources. Server-Sent Events of lange downloads leiden zelden tot rate limits (weinig verzoeken), maar kosten wel tijd \u2013 hier pas ik parallel beperkingen toe met `limit_conn` of implementeer ik bandbreedtestrategie\u00ebn. Waar mogelijk ontlast ik het systeem met <strong>Caching<\/strong> (bijv. statische assets, veelvoorkomende GET-verzoeken), zodat de limieten minder vaak in werking treden en gebruikers snellere antwoorden krijgen.<\/p>\n\n<h2>Operationele checklist<\/h2>\n\n<ul>\n  <li>Real-IP correct, sleutels gedefinieerd (IP\/token\/gebruiker)<\/li>\n  <li>Zones ruim bemeten, statistieken\/logbestanden aanwezig<\/li>\n  <li>snelheid\/burst afgestemd per padklasse, nodelay bewust ingesteld<\/li>\n  <li>Dry-run getest, 429-communicatie (Retry-After) ge\u00efmplementeerd<\/li>\n  <li>Uitzonderingen voor Health\/Webhooks, combinatie met limit_conn<\/li>\n  <li>Iteratieve heranalyse en waarschuwing bij afwijkingen<\/li>\n<\/ul>","protected":false},"excerpt":{"rendered":"<p>Ontdek hoe je met NGINX Rate Limiting je website kunt beschermen tegen botverkeer en aanvallen, en zo de beveiliging van je webserver kunt verbeteren. Inclusief praktische voorbeelden en best practices.<\/p>","protected":false},"author":1,"featured_media":21248,"comment_status":"","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"inline_featured_image":false,"footnotes":""},"categories":[834],"tags":[],"class_list":["post-21255","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-plesk-webserver-plesk-administration-anleitungen"],"acf":[],"_wp_attached_file":null,"_wp_attachment_metadata":null,"litespeed-optimize-size":null,"litespeed-optimize-set":null,"_elementor_source_image_hash":null,"_wp_attachment_image_alt":null,"stockpack_author_name":null,"stockpack_author_url":null,"stockpack_provider":null,"stockpack_image_url":null,"stockpack_license":null,"stockpack_license_url":null,"stockpack_modification":null,"color":null,"original_id":null,"original_url":null,"original_link":null,"unsplash_location":null,"unsplash_sponsor":null,"unsplash_exif":null,"unsplash_attachment_metadata":null,"_elementor_is_screenshot":null,"surfer_file_name":null,"surfer_file_original_url":null,"envato_tk_source_kit":null,"envato_tk_source_index":null,"envato_tk_manifest":null,"envato_tk_folder_name":null,"envato_tk_builder":null,"envato_elements_download_event":null,"_menu_item_type":null,"_menu_item_menu_item_parent":null,"_menu_item_object_id":null,"_menu_item_object":null,"_menu_item_target":null,"_menu_item_classes":null,"_menu_item_xfn":null,"_menu_item_url":null,"_trp_menu_languages":null,"rank_math_primary_category":null,"rank_math_title":null,"inline_featured_image":null,"_yoast_wpseo_primary_category":null,"rank_math_schema_blogposting":null,"rank_math_schema_videoobject":null,"_oembed_049c719bc4a9f89deaead66a7da9fddc":null,"_oembed_time_049c719bc4a9f89deaead66a7da9fddc":null,"_yoast_wpseo_focuskw":null,"_yoast_wpseo_linkdex":null,"_oembed_27e3473bf8bec795fbeb3a9d38489348":null,"_oembed_c3b0f6959478faf92a1f343d8f96b19e":null,"_trp_translated_slug_en_us":null,"_wp_desired_post_slug":null,"_yoast_wpseo_title":null,"tldname":null,"tldpreis":null,"tldrubrik":null,"tldpolicylink":null,"tldsize":null,"tldregistrierungsdauer":null,"tldtransfer":null,"tldwhoisprivacy":null,"tldregistrarchange":null,"tldregistrantchange":null,"tldwhoisupdate":null,"tldnameserverupdate":null,"tlddeletesofort":null,"tlddeleteexpire":null,"tldumlaute":null,"tldrestore":null,"tldsubcategory":null,"tldbildname":null,"tldbildurl":null,"tldclean":null,"tldcategory":null,"tldpolicy":null,"tldbesonderheiten":null,"tld_bedeutung":null,"_oembed_d167040d816d8f94c072940c8009f5f8":null,"_oembed_b0a0fa59ef14f8870da2c63f2027d064":null,"_oembed_4792fa4dfb2a8f09ab950a73b7f313ba":null,"_oembed_33ceb1fe54a8ab775d9410abf699878d":null,"_oembed_fd7014d14d919b45ec004937c0db9335":null,"_oembed_21a029d076783ec3e8042698c351bd7e":null,"_oembed_be5ea8a0c7b18e658f08cc571a909452":null,"_oembed_a9ca7a298b19f9b48ec5914e010294d2":null,"_oembed_f8db6b27d08a2bb1f920e7647808899a":null,"_oembed_168ebde5096e77d8a89326519af9e022":null,"_oembed_cdb76f1b345b42743edfe25481b6f98f":null,"_oembed_87b0613611ae54e86e8864265404b0a1":null,"_oembed_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_oembed_time_27aa0e5cf3f1bb4bc416a4641a5ac273":null,"_tldname":null,"_tldclean":null,"_tldpreis":null,"_tldcategory":null,"_tldsubcategory":null,"_tldpolicy":null,"_tldpolicylink":null,"_tldsize":null,"_tldregistrierungsdauer":null,"_tldtransfer":null,"_tldwhoisprivacy":null,"_tldregistrarchange":null,"_tldregistrantchange":null,"_tldwhoisupdate":null,"_tldnameserverupdate":null,"_tlddeletesofort":null,"_tlddeleteexpire":null,"_tldumlaute":null,"_tldrestore":null,"_tldbildname":null,"_tldbildurl":null,"_tld_bedeutung":null,"_tldbesonderheiten":null,"_oembed_ad96e4112edb9f8ffa35731d4098bc6b":null,"_oembed_8357e2b8a2575c74ed5978f262a10126":null,"_oembed_3d5fea5103dd0d22ec5d6a33eff7f863":null,"_eael_widget_elements":null,"_oembed_0d8a206f09633e3d62b95a15a4dd0487":null,"_oembed_time_0d8a206f09633e3d62b95a15a4dd0487":null,"_aioseo_description":null,"_eb_attr":null,"_eb_data_table":null,"_oembed_819a879e7da16dd629cfd15a97334c8a":null,"_oembed_time_819a879e7da16dd629cfd15a97334c8a":null,"_acf_changed":null,"_wpcode_auto_insert":null,"_edit_last":null,"_edit_lock":null,"_oembed_e7b913c6c84084ed9702cb4feb012ddd":null,"_oembed_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_time_bfde9e10f59a17b85fc8917fa7edf782":null,"_oembed_03514b67990db061d7c4672de26dc514":null,"_oembed_time_03514b67990db061d7c4672de26dc514":null,"rank_math_news_sitemap_robots":null,"rank_math_robots":null,"_eael_post_view_count":"69","_trp_automatically_translated_slug_ru_ru":null,"_trp_automatically_translated_slug_et":null,"_trp_automatically_translated_slug_lv":null,"_trp_automatically_translated_slug_fr_fr":null,"_trp_automatically_translated_slug_en_us":null,"_wp_old_slug":null,"_trp_automatically_translated_slug_da_dk":null,"_trp_automatically_translated_slug_pl_pl":null,"_trp_automatically_translated_slug_es_es":null,"_trp_automatically_translated_slug_hu_hu":null,"_trp_automatically_translated_slug_fi":null,"_trp_automatically_translated_slug_ja":null,"_trp_automatically_translated_slug_lt_lt":null,"_elementor_edit_mode":null,"_elementor_template_type":null,"_elementor_version":null,"_elementor_pro_version":null,"_wp_page_template":null,"_elementor_page_settings":null,"_elementor_data":null,"_elementor_css":null,"_elementor_conditions":null,"_happyaddons_elements_cache":null,"_oembed_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_time_75446120c39305f0da0ccd147f6de9cb":null,"_oembed_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_time_3efb2c3e76a18143e7207993a2a6939a":null,"_oembed_59808117857ddf57e478a31d79f76e4d":null,"_oembed_time_59808117857ddf57e478a31d79f76e4d":null,"_oembed_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_time_965c5b49aa8d22ce37dfb3bde0268600":null,"_oembed_81002f7ee3604f645db4ebcfd1912acf":null,"_oembed_time_81002f7ee3604f645db4ebcfd1912acf":null,"_elementor_screenshot":null,"_oembed_7ea3429961cf98fa85da9747683af827":null,"_oembed_time_7ea3429961cf98fa85da9747683af827":null,"_elementor_controls_usage":null,"_elementor_page_assets":[],"_elementor_screenshot_failed":null,"theplus_transient_widgets":null,"_eael_custom_js":null,"_wp_old_date":null,"_trp_automatically_translated_slug_it_it":null,"_trp_automatically_translated_slug_pt_pt":null,"_trp_automatically_translated_slug_zh_cn":null,"_trp_automatically_translated_slug_nl_nl":null,"_trp_automatically_translated_slug_pt_br":null,"_trp_automatically_translated_slug_sv_se":null,"rank_math_analytic_object_id":null,"rank_math_internal_links_processed":"1","_trp_automatically_translated_slug_ro_ro":null,"_trp_automatically_translated_slug_sk_sk":null,"_trp_automatically_translated_slug_bg_bg":null,"_trp_automatically_translated_slug_sl_si":null,"litespeed_vpi_list":null,"litespeed_vpi_list_mobile":null,"rank_math_seo_score":null,"rank_math_contentai_score":null,"ilj_limitincominglinks":null,"ilj_maxincominglinks":null,"ilj_limitoutgoinglinks":null,"ilj_maxoutgoinglinks":null,"ilj_limitlinksperparagraph":null,"ilj_linksperparagraph":null,"ilj_blacklistdefinition":null,"ilj_linkdefinition":null,"_eb_reusable_block_ids":null,"rank_math_focus_keyword":"NGINX Rate","rank_math_og_content_image":null,"_yoast_wpseo_metadesc":null,"_yoast_wpseo_content_score":null,"_yoast_wpseo_focuskeywords":null,"_yoast_wpseo_keywordsynonyms":null,"_yoast_wpseo_estimated-reading-time-minutes":null,"rank_math_description":null,"surfer_last_post_update":null,"surfer_last_post_update_direction":null,"surfer_keywords":null,"surfer_location":null,"surfer_draft_id":null,"surfer_permalink_hash":null,"surfer_scrape_ready":null,"_thumbnail_id":"21248","footnotes":null,"_links":{"self":[{"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/posts\/21255","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/comments?post=21255"}],"version-history":[{"count":0,"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/posts\/21255\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/media\/21248"}],"wp:attachment":[{"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/media?parent=21255"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/categories?post=21255"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/webhosting.de\/nl\/wp-json\/wp\/v2\/tags?post=21255"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}