Taux NGINX Limiting bloque les requêtes automatisées, limite les pics de trafic et protège les points de terminaison de connexion, d’API et de formulaires contre les bots et les attaques. Je vais te montrer comment définir des limites et les appliquer à Protection contre les bots et en tire un concept de sécurité fiable pour les sites web très fréquentés.
Points centraux
Essentiel voici les messages clés :
- Limites de taux freinent les attaques malveillantes et protègent les ressources backend.
- Burst/sans délai absorbent les pics de trafic légitimes sans bloquer les utilisateurs.
- zones distinguer les humains des bots en fonction de limites différentes.
- Enregistrement fournit des données permettant d'affiner les limites de manière itérative.
- Intégration avec un pare-feu applicatif web (WAF), une protection contre les attaques DDoS et une surveillance, ce qui renforce son efficacité.
Pourquoi la limitation de débit permet de stopper les attaques à un stade précoce
Les attaquants misent sur une Taux de requêtes, afin d'exploiter abusivement les formulaires de connexion, de surcharger les API ou d'extraire automatiquement du contenu. Je limite donc le nombre de requêtes par clé – généralement par adresse IP – et je décide si je les limite, si je les retarde ou si je leur renvoie un code d'erreur 429. Je protège ainsi le processeur, la base de données et la logique applicative du trafic généré par les bots, tout en laissant passer les utilisateurs légitimes. Les chemins particulièrement sensibles tels que /login, /auth, /xmlrpc.php ou les recherches gourmandes en ressources en bénéficient grandement. La source de cette méthode est la Documentation Nginx Concernant le module ngx_http_limit_req_module.
Fonctionnement du module NGINX en pratique
Le module fonctionne selon le principe Leaky-BucketPrincipe : pour chaque clé, NGINX enregistre les valeurs de comptage dans une zone et les compare au débit autorisé. Les clés typiques sont $binary_remote_addr pour les adresses IP, les jetons pour les clés API ou les valeurs dérivées via map. Si un client dépasse de manière persistante le débit autorisé et la limite du tampon de rafales, NGINX rejette la requête avant qu’elle n’atteigne le backend. Cela permet d’économiser du temps de calcul et de réduire les latences pour les véritables visiteurs. Je configure la réponse avec le code d’état 429 « Too Many Requests » ou, au choix, un autre Code d'état hum.
Configuration : explications étape par étape
Je commence par créer une zone dans la section http, je définis un débit modéré et je l'active de manière ciblée sur les chemins sensibles. Pour les pics de trafic de courte durée, je définis un « burst », éventuellement avec « nodelay », afin d'éviter les rejets brusques. Je teste ensuite le tout en environnement de préproduction et j'analyse les journaux avant de passer en production. Ainsi, je ne risque pas de bloquer inutilement de vrais utilisateurs. Un exemple concis illustre la Syntaxe tangible :
# http {}
limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=10r/s;
server {
location /api/ {
limit_req zone=req_limit_per_ip burst=20 nodelay;
limit_req_status 429;
}
location /login {
limit_req zone=req_limit_per_ip burst=5;
limit_req_status 429;
}
}
Protection contre les bots grâce à des zones et à la logique des agents utilisateurs
Les limites basées sur les adresses IP sont rarement suffisantes contre les botnets distribués ; c'est pourquoi je sépare le trafic en zones: Les utilisateurs bénéficient de limites plus généreuses, tandis que les robots d'indexation génériques sont soumis à des règles plus strictes. Grâce à map, j'évalue les user-agents, identifie les robots exemptés tels que Googlebot et leur attribue des limites spécifiques, étroitement surveillées. Pour les scrapers anonymes, je fixe des limites strictes sur les chemins coûteux. Si je détecte des schémas récurrents, je renforce dynamiquement la rigueur jusqu’à ce que les Taux est de nouveau dans la norme.
Réglages avancés : débit, rafale, nodelay et codes d'état
Le paramètre « Rate » régule le débit par seconde, le paramètre « Burst » autorise des mises en mémoire tampon de courte durée, et « nodelay » détermine si je préfère la mise en mémoire tampon ou le passage immédiat. Je commence modérément, par exemple à 10 r/s avec un burst de 20 sur les API, puis j'affine les paramètres après analyse des logs. Pour les routes de connexion, je règle par exemple 1 r/s avec un petit burst afin de freiner les attaques par force brute. En cas de dépassement, je renvoie un code 429, car les clients peuvent ainsi s'en sortir et que la logique de réessai fonctionne correctement. Dans certains cas particuliers, j'utilise des codes alternatifs lorsque les clients le demandent.
Aperçu dans le tableau : directives et utilisation
La suivante Tableau résume les principales directives et indique dans quels cas elles sont pertinentes.
| directive | Effet | Exemple | Utilisation typique |
|---|---|---|---|
| limit_req_zone | Définit la clé, la zone et Taux fermement | limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s; | Base par adresse IP, jeton ou agent utilisateur |
| limit_req | Active la limite dans Emplacement/Serveur | limit_req zone=perip burst=20 nodelay; | Réglage fin par chemin ou par vHost |
| limit_req_status | Définit le code HTTP dépassement | limit_req_status 429; | Comportement correct du client et nouvelles tentatives |
| map | Redirige les requêtes vers zones à l'adresse suivante : | map $http_user_agent $is_bot {…} | Distinction entre robot et humain en fonction de l'agent utilisateur |
Pratique : sécuriser de manière ciblée le point d'accès à la connexion
J'applique des restrictions très strictes à /login, car les bots tentent de deviner des mots de passe avec un niveau de Fréquence Tester différentes options. 1 requête par seconde avec un burst de 3 permet d'éviter les tentatives de deviner le mot de passe de manière massive, sans trop pénaliser les véritables utilisateurs. De plus, je consigne les échecs répétés dans un journal afin de bloquer temporairement les adresses IP. Associé à l’authentification à deux facteurs (2FA) et, en option, à un captcha, cela réduit sensiblement la charge sur la base de données et la gestion des sessions. Je limite ainsi le nombre de tentatives infructueuses et garantis la Accès prêt et stable.
Dans la pratique : fournir des API de manière équitable et contrôlée
Les API nécessitent des Cotes, afin d'éviter que certains clients ne monopolisent l'ensemble du débit. Pour les routes générales, je fixe la valeur à 10 r/s avec un pic de 20 ; pour les points de terminaison coûteux, j’applique des valeurs plus strictes. Lorsque des jetons ou des clés API sont disponibles, je limite l’accès par jeton plutôt que par adresse IP. Cela garantit l’équité entre les clients et empêche les abus. Pour une analyse plus approfondie, consultez ma note sur Limitation du débit de l'API, qui replace ce concept dans un contexte plus large.
Suivi, enregistrement et affinement itératif
J'enregistre 429 réponses, y compris Clé (par exemple, une adresse IP ou un jeton) et le chemin d'accès, afin d'identifier des schémas récurrents. Des pics sur un petit nombre de chemins indiquent un scraping ou une attaque par force brute ; une charge répartie suggère la présence de botnets. Grâce à ces données, je n’applique des limites que là où c’est nécessaire et je minimise les faux positifs. Des tableaux de bord indiquant les débits, le taux d’erreur et la latence me permettent de visualiser l’impact de chaque modification. Ainsi, la Performance élevé, tandis que la protection s'améliore.
Intégration dans un concept de protection global
Je considère la limitation du débit comme une première mesure efficace couche, mais je l'associe à des règles WAF, à la réputation IP et au renforcement TLS. Contre les attaques de volume, une protection DDoS en amont permet de filtrer le trafic au niveau du réseau avant que NGINX n'ait à intervenir. Je mesure en continu les indicateurs, je configure des alertes en cas de pics inhabituels et je réagis en mettant à jour les règles. C’est ainsi qu’un réseau de protection résilient se construit à partir de plusieurs éléments. Ces articles fournissent un aperçu concret : Stratégies de lutte contre les attaques DDoS.
Modèles de configuration concrets pour les bots par rapport aux humains
Je classe les visiteurs par catégories à l'aide de « map » et je les redirige vers leurs propres zones. Les robots d'indexation connus se voient attribuer des limites modérées, tandis que les agents génériques sont soumis à des limites plus strictes. Je reste plus strict pour les chemins tels que /search ou /report, car ils mobilisent beaucoup de ressources CPU. En cas d’infractions répétées, je n’augmente pas les limites, mais j’applique un blocage temporaire ou je transfère la vérification vers un module de détection des bots. Ainsi, la Taux d'utilisation abusive faible, sans perturber les moteurs de recherche.
Exemple : deux zones et mise en correspondance des agents utilisateur
L'extrait suivant montre la séparation selon Agent utilisateur et l’attribution de limites adaptées. Je combine cela avec des codes d’état différenciés et des champs de journalisation afin de mesurer précisément l’impact. Les bots utilisant un agent générique sont dirigés vers la zone stricte. Les utilisateurs humains ou les robots d’exploration vérifiés bénéficient de la zone plus souple. Cette approche permet une prévisibilité Débits par classe :
map $http_user_agent $is_bot {
default 0;
"~*googlebot" 0;
"~*bingbot" 0;
"~*crawler|scraper|bot" 1;
}
limit_req_zone $binary_remote_addr zone=human:10m rate=10r/s;
limit_req_zone $binary_remote_addr zone=bot:10m rate=1r/s;
server {
location / {
if ($is_bot) {
limit_req zone=bot burst=5;
}
if ($is_bot = 0) {
limit_req zone=human burst=20 nodelay;
}
limit_req_status 429;
}
}
Gestion des erreurs : communiquer correctement le code d'erreur 429
Je donne une réponse claire sur Limits Réponse en précisant quand il est judicieux de réessayer. Pour les API, cela inclut l’en-tête « Retry-After » valide, afin que les clients appliquent la stratégie de « backoff ». Les utilisateurs humains reçoivent une brève explication sans détails techniques. Cela réduit le nombre de tickets et garantit un comportement compréhensible. Une solution claire UX rend les limites acceptables et évite la frustration.
Hébergeur, réseau et noyau : renforcer les fondements
Un trafic légitime important et les mesures de protection exigent des solutions fiables Ressources et des paramètres par défaut pertinents au niveau du réseau. Je veille à utiliser des versions récentes de NGINX, à disposer d'une mémoire vive suffisante pour les zones et à mettre en place des fonctions de protection contre les attaques de transport. Pour se prémunir contre les attaques SYN-Flood, il est utile d'activer Cookies TCP SYN dans le noyau, afin d'éviter que les connexions ne restent bloquées. Au final, cela soulage NGINX d'une charge inutile. Je concentre ainsi les limites au niveau de la couche HTTP et maintiens le Débit stable.
En bref : voici comment j'utilise efficacement la limitation de débit avec NGINX
Je limite les demandes en fonction de Clé, je protège les chemins critiques et tiens les bots à distance grâce à des zones strictement contrôlées. Les fonctions « Burst » et « nodelay » permettent d’autoriser les pics de trafic légitimes sans encourager les abus. À partir des journaux 429, je calibre en permanence les valeurs et ne renforce les limites que lorsque cela s’avère nécessaire. Combiné au WAF, à la protection DDoS, à la surveillance et au durcissement du noyau, cela donne naissance à un concept de protection résilient. Ceux qui le mettent en œuvre de manière cohérente réduisent considérablement le trafic des bots et préservent Performance même sous charge.
Éléments souvent absents dans la pratique
Dans de nombreuses configurations, il manque certains éléments essentiels qui renforcent sensiblement l'efficacité de la limitation du débit :
- Adresse IP réelle du client derrière des proxys: Sans une gestion correcte des adresses IP réelles, NGINX limite souvent l'adresse IP de l'équilibreur de charge ; les limites s'appliquent alors à tous les utilisateurs regroupés en aval.
- Essais à sec (Dry-Run): Les limites sont activées „ à l'aveugle “. Il vaut mieux commencer par enregistrer uniquement le nombre de fois où une limite aurait été déclenchée.
- Clés à grains fins: Plutôt que de se limiter à l'adresse IP, il est préférable de fixer des limites par jeton API, par session ou par utilisateur afin de garantir une plus grande équité.
- Interaction avec limit_conn: Les connexions parallèles et les taux de requêtes permettent de couvrir différents types d'abus.
- Exceptions ciblées: Les bilans de santé, les webhooks ou les services internes nécessitent souvent des limites plus souples, voire aucune limite.
Proxy inverse : analyser en toute sécurité l'adresse IP réelle du client
Si NGINX est placé derrière un équilibreur de charge, je configure les directives Real-IP afin que $binary_remote_addr reflète l'adresse réelle du client. Je ne fais confiance qu'aux réseaux qui m'appartiennent et j'active l'évaluation récursive :
http {
# Plages d'adresses IP de proxy de confiance (exemple)
set_real_ip_from 10.0.0.0/8;
set_real_ip_from 192.168.0.0/16;
# Ajouter le cas échéant des plages LB/CDN publiques
real_ip_header X-Forwarded-For;
real_ip_recursive on;
limit_req_zone $binary_remote_addr zone=perip:20m rate=10r/s;
}
Sans ce paramètre, une limite risque sinon d'affecter à tort de nombreux utilisateurs en même temps. Une fois la configuration effectuée, je vérifie dans les journaux d'accès si l'adresse IP du client attendue apparaît.
Stratégie clé : IP, utilisateur, jeton et chemin d'accès
Le choix de la clé détermine l'équité et l'efficacité. Voici quelques modèles qui ont fait leurs preuves :
- Pro IP ($binary_remote_addr) : rapidement opérationnel, adapté à /login et aux points de terminaison anonymes.
- Par jeton API: Équité entre les clients ; protège contre le regroupement NAT. J'extrais les jetons via map.
- Par classe de chemin: Limiter séparément les points de terminaison coûteux, par exemple /search davantage que /status.
map $http_authorization $api_token {
default "";
"~*^Bearer\s+(.+)$" $1;
}
limit_req_zone $api_token zone=per_token:30m rate=5r/s;
server {
location /api/ {
# Ne s'applique que si un jeton est présent
limit_req zone=per_token burst=10;
limit_req_status 429;
}
}
Important : une cardinalité élevée des clés consomme de la mémoire dans la zone. Prévoyez des tampons et surveillez l'occupation mémoire.
Réservoirs et dimensionnement des zones
La zone stocke des métadonnées pour chaque clé active. L'espace occupé par chaque entrée est de quelques dizaines d'octets, plus la surcharge. J'en déduis donc que :
- Lorsque le nombre d'adresses IP/jetons simultanés est élevé, j'opte pour des zones plus grandes, par exemple de 50 à 100 Mo.
- Je commence par un réglage plutôt généreux et je consulte les journaux NGINX : le message „ shared memory zone is full “ indique qu'il faut affiner les paramètres.
- Les clés inutilisées expirent après une courte période d'inactivité ; les pics sont plus importants que la moyenne quotidienne.
Utiliser « Burst » et « nodelay » avec précision
Sans nodelay NGINX classe les dépassements au sein du tampon de rafale et différé Demandes. Avec nodelay Les requêtes en rafale autorisées sont immédiatement acceptées, tandis que celles en excès sont rejetées. Ma démarche :
- Parcours interactifs (HTML) : plutôt sans « nodelay », afin de générer de courts délais d'attente plutôt que des erreurs 429 définitives.
- APIs: souvent avec « nodelay », afin que les clients reçoivent clairement le code d'erreur 429 et appliquent la stratégie de « backoff ».
- Des terminaux coûteux: petite rafale pour lisser les pics du backend.
Simulation, niveau de journalisation et analyse
Avant d'activer les limites, j'active le mode « Dry-Run » et je règle le niveau de journalisation. Cela me permet d'en voir les effets sans prendre de risque :
server {
location /api/ {
limit_req zone=perip burst=20;
limit_req_dry_run on ; # : enregistrer uniquement, ne pas bloquer
limit_req_log_level notice ; # : moins grave que ' error '
}
}
J'analyse ensuite les données d'accès sur une période de 3 à 7 jours, j'identifie les points sensibles, j'ajuste le débit et les pics de trafic, puis je désactive le « Dry-Run ».
429 : un transport propre : HTML, JSON et Retry-After
Pour garantir une bonne expérience utilisateur (UX), je fais la distinction entre les navigateurs et les clients API, et je configure Retry-After. Voici comment je communique clairement mes limites :
map $http_accept $wants_json {
default 0;
"~*application/json|/json" 1;
}
server {
error_page 429 = @rate_limited;
location @rate_limited {
add_header Retry-After 2 always;
if ($wants_json) {
add_header Content-Type application/json;
return 429 '{"error":"too_many_requests","retry_after":2}';
}
return 429 "Veuillez réessayer plus tard.";
}
}
Les API peuvent ainsi réagir de manière programmatique, et les utilisateurs reçoivent un message clair.
Combiner limit_req et limit_conn
limit_req débit par tranche horaire, limit_conn limite le nombre de connexions simultanées. Pour lutter contre les téléchargements, les clients « bavards » ou les flux HTTP/2, je combine les deux méthodes suivantes :
limit_conn_zone $binary_remote_addr zone=perip_conn:10m;
server {
location /api/ {
limit_req zone=perip burst=20 nodelay;
limit_conn zone=perip_conn 20; # max. 20 connexions simultanées par adresse IP
}
}
Cela me permet d'éviter que quelques clients, bien qu'ils respectent le débit, ne monopolisent les ressources en établissant un trop grand nombre de connexions simultanées.
Exceptions, contrôles d'intégrité et chemins internes
Tous les chemins ne nécessitent pas de limites. Les Health Checks (/healthz), les webhooks internes ou les callbacks de paiement disposent de leurs propres emplacements sans `limit_req`, ou avec des valeurs moins strictes :
server {
# aucune limite pour les contrôles d'intégrité
location = /healthz { return 200 "ok"; }
# limites souples pour les callbacks de paiement
location /webhooks/pay/ {
limit_req zone=perip burst=5;
}
# protection stricte pour la connexion
location = /login {
limit_req zone=perip rate=1r/s burst=3;
}
}
Les exceptions granulaires réduisent les faux positifs et garantissent la stabilité des intégrations.
Un routage par zone plus robuste, sans « magie » If
Pour la distinction „ bot vs. humain “, je préfère utiliser des redirigements internes via des emplacements nommés. Cela rend la configuration claire et prévisible :
map $http_user_agent $is_bot {
default 0;
"~*googlebot|bingbot" 0;
"~*crawler|scraper|bot" 1;
}
limit_req_zone $binary_remote_addr zone=human:20m rate=10r/s;
limit_req_zone $binary_remote_addr zone=bot:10m rate=1r/s;
server {
error_page 418 = @bot;
location / {
if ($is_bot) { return 418; } # redirection interne
limit_req zone=human burst=20 nodelay;
limit_req_status 429;
try_files $uri $uri/ /index.html;
}
location @bot {
limit_req zone=bot burst=5;
limit_req_status 429;
}
}
Ainsi, les bots se retrouvent de manière déterministe dans la zone stricte, tandis que les humains se retrouvent dans la zone détendue – sans que les deux limites ne s’appliquent simultanément.
Tester, mesurer, essayer : une démarche pragmatique
- Staging: Choisir un débit/une rafale prudents, activer le « Dry-Run », tester la charge synthétique sur le chemin critique.
- Tests de fumée: Générer de courtes rafales à l'aide de curl ou de Lasttools et vérifier le comportement en cas de code 429 ou de délai.
- Pilote de production: Commencer par l'appliquer à des sites spécifiques, puis surveiller de près les journaux.
- Affûtage itératif: Ne fixer des limites que là où des tendances se dessinent ; réduire au minimum les fausses alertes.
Exemple # : test de rafale rapide avec curl
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/login & done; wait
Fréquences en minutes plutôt qu'en secondes et chemins granulaires
NGINX autorise des fréquences en secondes ou en minutes (r/s, r/m). En cas d'utilisation abusive de la connexion, je fixe souvent la limite à 60 requêtes par minute au lieu de 1 requête par seconde, afin d'autoriser les doubles clics légitimes de courte durée tout en limitant les envois en rafale. Les chemins coûteux se voient attribuer des limites plus strictes que les chemins bon marché. Exemple :
limit_req_zone $binary_remote_addr zone=perip_min:20m rate=60r/m;
server {
location /search/ {
limit_req zone=perip_min burst=10; # plus strict
}
location /status {
# pas de limite – bon marché et utilisé en interne
return 200;
}
}
Les pièges et comment je les évite
- Mauvaise clé: Derrière des proxys sans adresse IP réelle, je limite par inadvertance tous les utilisateurs en même temps.
- Des zones trop petites: Le message „ zone is full “ entraîne un comportement imprévisible – prévoir des dimensions généreuses.
- Une limite pour tout: Chaque parcours nécessite des valeurs différentes ; une approche universelle est source de frustration.
- Pas de suivi: Sans analyse 429, les erreurs de configuration passent inaperçues.
- Liste blanche étendue: Des exceptions trop larges ouvrent la porte à toutes sortes de problèmes – il faut établir une liste blanche ciblée, temporaire et transparente.
Particularités liées à HTTP/2, SSE et la mise en cache
HTTP/2 regroupe les requêtes sur un petit nombre de connexions ; limit_conn reste néanmoins pertinent, car les flux consomment des ressources. Les événements « Server-Sent » ou les téléchargements longs déclenchent rarement des limites de débit (peu de requêtes), mais prennent du temps ; dans ce cas, j’applique une limitation parallèle avec `limit_conn` ou je mets en œuvre des stratégies de bande passante. Dans la mesure du possible, je décharge le système avec Mise en cache (par exemple, les ressources statiques, les requêtes GET fréquentes), afin que les limites soient moins souvent appliquées et que les utilisateurs obtiennent des réponses plus rapides.
Liste de contrôle opérationnelle
- IP réelle correcte, clés définies (IP/jeton/utilisateur)
- Zones aux dimensions généreuses, métriques/journaux disponibles
- débit/rafale ajustés par classe de chemin, nodelay défini intentionnellement
- Test de simulation effectué, communication 429 (Retry-After) mise en œuvre
- Exceptions pour Health/Webhooks, combinaison avec limit_conn
- Affinage itératif et alerte en cas d'anomalies


