La limitation de débit est l’une des premières lignes de défense les plus efficaces contre les attaques DDoS, les tentatives de connexion par brute force et l’abus d’API. Nginx inclut deux puissants modules intégrés — limit_req et limit_conn — qui vous permettent de limiter le trafic entrant sans installer de logiciel tiers. Dans ce guide, vous configurerez les deux modules avec des exemples prêts pour la production, ajouterez des IP de confiance à la liste blanche et testerez le tout avant la mise en service.

Prérequis

  • Nginx 1.18+ installé sur un serveur Linux (Ubuntu/Debian ou RHEL/CentOS)
  • Accès root ou sudo
  • Connaissance de base des blocs server Nginx et de la syntaxe de configuration
  • Un outil de test de charge : ab (Apache Bench), wrk ou curl

Comprendre la limitation de débit Nginx

Le module limit_req de Nginx implémente l’algorithme du seau percé (leaky bucket). Imaginez un seau avec un petit trou au fond : l’eau (les requêtes) s’y déverse à des débits variables, mais s’écoule à un débit fixe. Si le seau déborde, l’excédent d’eau (de requêtes) est rejeté.

Deux concepts clés gouvernent la configuration :

  • Zone : Une zone de mémoire partagée qui suit le nombre de requêtes par clé (généralement l’IP du client). Définie une fois dans le bloc http et référencée dans les blocs server ou location.
  • Débit : Le débit autorisé exprimé en requêtes par seconde (r/s) ou par minute (r/m). Nginx convertit internement tout en granularité par seconde.

Le module limit_conn fonctionne différemment — il plafonne le nombre de connexions simultanées ouvertes depuis une seule clé plutôt que le débit des requêtes.

Configuration de la limitation du débit des requêtes

Commencez par définir les zones dans le bloc http de /etc/nginx/nginx.conf :

http {
    # Zone générale : 10 requêtes/seconde par IP, 10 Mo de mémoire partagée
    limit_req_zone $binary_remote_addr zone=general:10m rate=10r/s;

    # Zone stricte pour les points de connexion : 1 requête/seconde par IP
    limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s;

    # Renvoyer 429 au lieu du 503 par défaut
    limit_req_status 429;
}

La variable $binary_remote_addr stocke les IP des clients dans un format binaire compact — 10 Mo contiennent environ 160 000 adresses.

Maintenant, appliquez ces zones dans votre bloc server :

server {
    listen 80;
    server_name example.com;

    # Limitation de débit générale pour toutes les requêtes
    location / {
        limit_req zone=general burst=20 nodelay;
        proxy_pass http://backend;
    }

    # Limite stricte sur la page de connexion
    location /wp-login.php {
        limit_req zone=login burst=3 nodelay;
        proxy_pass http://backend;
    }

    # Page d'erreur 429 personnalisée
    error_page 429 /rate_limit.html;
    location = /rate_limit.html {
        internal;
        return 429 '{"error": "Too many requests. Please retry after a few seconds."}';
        default_type application/json;
    }
}

Le paramètre burst définit combien de requêtes excédentaires peuvent être mises en file d’attente avant que Nginx ne commence à les rejeter. Avec nodelay, les requêtes en rafale sont traitées immédiatement au lieu d’être espacées — cela offre une meilleure expérience utilisateur pour les pics de trafic légitimes.

Configuration des limites de connexion

La limitation de connexion empêche une seule IP de maintenir trop de sockets ouverts simultanément, ce qui est le mécanisme central des attaques de type slowloris :

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;
    limit_conn_status 429;
}

server {
    # Maximum 10 connexions simultanées par IP
    limit_conn addr 10;

    # Limiter la bande passante par connexion (optionnel)
    limit_rate 100k;

    location /downloads/ {
        limit_conn addr 2;
        limit_rate 50k;
    }
}

Comparaison des méthodes de limitation de débit

Fonctionnalitélimit_reqlimit_connWAF externe (Cloudflare/ModSecurity)
ContrôleDébit des requêtes (req/s)Connexions simultanéesLes deux + motifs couche 7
AlgorithmeSeau percéCompteur de connexionsVariable (ML, signatures)
GranularitéPar clé (IP, en-tête, etc.)Par cléPar règle, géo, ASN
Impact sur les performancesMinimalMinimalLégère latence ajoutée
Efficacité contre DDoSBonne contre les floods L7Bonne contre slowlorisMeilleure contre volumétrique
ConfigurationConfiguration Nginx uniquementConfiguration Nginx uniquementService/agent séparé
CoûtGratuitGratuitNiveau gratuit ou payant

Pour la plupart des serveurs, la combinaison de limit_req + limit_conn localement avec un WAF externe offre une défense en profondeur.

Scénario concret

Vous avez un serveur de production exécutant WordPress derrière Nginx. Vos journaux d’accès montrent des milliers de requêtes POST ciblant /wp-login.php et /xmlrpc.php depuis des IP rotatives — une attaque par brute force distribuée. L’utilisation du CPU grimpe en flèche et les utilisateurs légitimes voient des délais d’attente dépassés.

Voici une configuration de production qui résout ce problème :

http {
    limit_req_zone $binary_remote_addr zone=wp_login:10m rate=1r/s;
    limit_req_zone $binary_remote_addr zone=xmlrpc:10m rate=1r/m;
    limit_req_zone $binary_remote_addr zone=general:10m rate=30r/s;
    limit_req_status 429;
    limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
}

server {
    listen 443 ssl;
    server_name example.com;

    limit_conn conn_limit 15;

    location / {
        limit_req zone=general burst=50 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }

    location = /wp-login.php {
        limit_req zone=wp_login burst=3 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }

    # Bloquer xmlrpc.php presque entièrement — 1 requête par minute
    location = /xmlrpc.php {
        limit_req zone=xmlrpc burst=1 nodelay;
        proxy_pass http://127.0.0.1:8080;
    }
}

Après avoir appliqué cette configuration et exécuté nginx -t && systemctl reload nginx, le trafic de brute force reçoit des réponses 429 tandis que les utilisateurs légitimes continuent de s’authentifier normalement.

Liste blanche des IP de confiance

Les systèmes de surveillance, les vérifications de santé et les réseaux de bureau doivent contourner les limites de débit. Utilisez le module geo pour créer une liste blanche :

http {
    geo $rate_limit_key {
        default         $binary_remote_addr;
        10.0.0.0/8      "";    # Réseau interne
        192.168.1.0/24   "";   # Réseau de bureau
        203.0.113.50     "";   # Serveur de surveillance
    }

    # Clé vide = pas de suivi = pas de limitation de débit
    limit_req_zone $rate_limit_key zone=general:10m rate=10r/s;
}

Lorsque $rate_limit_key est résolu en une chaîne vide, Nginx ignore complètement le suivi de la limitation de débit pour cette requête.

Test des limites de débit

Testez toujours en pré-production avant de déployer en production. Utilisez limit_req_dry_run on pour journaliser les violations sans bloquer :

location / {
    limit_req zone=general burst=20 nodelay;
    limit_req_dry_run on;  # Journaliser uniquement, ne pas bloquer
}

Ensuite, générez du trafic avec Apache Bench :

# Envoyer 100 requêtes avec 10 connexions simultanées
ab -n 100 -c 10 https://staging.example.com/

# Vérifier combien ont été limitées en débit
grep "limiting requests" /var/log/nginx/error.log | wc -l

# Vérifier les réponses 429 dans le journal d'accès
awk '$9 == 429' /var/log/nginx/access.log | wc -l

Ou utilisez wrk pour un test de charge soutenu :

wrk -t4 -c50 -d30s https://staging.example.com/

Une fois que vous êtes satisfait des seuils, supprimez la directive limit_req_dry_run et rechargez Nginx.

Pièges et cas particuliers

  • Clients derrière un CDN/proxy : Si Nginx se trouve derrière Cloudflare, un répartiteur de charge ou un proxy inverse, $binary_remote_addr sera l’IP du proxy — pas celle du client. Utilisez set_real_ip_from et real_ip_header pour extraire l’IP réelle du client :
set_real_ip_from 173.245.48.0/20;  # Plage IP Cloudflare
set_real_ip_from 103.21.244.0/22;
real_ip_header CF-Connecting-IP;
  • IP partagées (NAT/entreprise) : Des limites de débit agressives peuvent bloquer des bureaux entiers derrière une seule IP NAT. Définissez des valeurs de burst raisonnables et ajoutez les plages d’entreprise connues à la liste blanche.
  • Épuisement de la mémoire de zone : Si la zone manque de mémoire, Nginx renvoie 503 pour toutes les nouvelles clés. Surveillez l’utilisation de la zone et dimensionnez en conséquence — 1 Mo contient environ 16 000 adresses IP.
  • Malentendu entre débit et burst : Un débit de 10r/s avec burst=20 ne permet pas 30 requêtes/seconde. Il autorise une file d’attente de 20 requêtes qui se vide à 10/s. Sans nodelay, les requêtes en file d’attente subissent un délai artificiel.
  • Bruit dans les journaux : La limitation de débit à fort trafic remplit rapidement les journaux d’erreurs. Utilisez un error_log séparé pour les emplacements limités en débit ou ajustez les niveaux de journalisation.

Résumé

  • Utilisez limit_req pour limiter le débit des requêtes avec l’algorithme du seau percé — idéal pour les pages de connexion, les API et la protection générale contre les floods.
  • Utilisez limit_conn pour plafonner les connexions simultanées par IP — efficace contre les attaques slowloris et l’épuisement des ressources.
  • Changez toujours le code de statut par défaut en 429 avec limit_req_status 429.
  • Ajoutez les IP internes à la liste blanche à l’aide du module geo pour que la surveillance et les vérifications de santé ne soient jamais bloquées.
  • Testez avec limit_req_dry_run on et des outils comme ab ou wrk avant de mettre en application en production.
  • Tenez compte des proxies et CDN en configurant real_ip_header pour identifier l’IP réelle du client.
  • Combinez la limitation de débit Nginx avec Fail2ban et un WAF externe pour une défense en profondeur.

Articles Connexes