Limitazione della frequenza di NGINX: protezione efficace dal traffico generato dai bot e dagli attacchi

Tariffa NGINX Limiting blocca le richieste automatizzate, riduce i picchi di carico e protegge gli endpoint di login, API e moduli da bot e attacchi. Ti mostro come definire i limiti e come applicarli a Protezione dai bot lo utilizza per elaborare un solido sistema di sicurezza per siti web molto frequentati.

Punti centrali

Essenziale Ecco i punti chiave:

  • Limiti tariffari frenano gli attacchi malevoli e proteggono le risorse di backend.
  • Burst/senza ritardo intercettano i picchi legittimi senza bloccare gli utenti.
  • zone distinguere le persone dai bot in base a limiti diversi.
  • Registrazione fornisce dati per affinare i limiti in modo iterativo.
  • Integrazione con WAF, protezione DDoS e monitoraggio, ne aumenta l'efficacia.

Perché il rate limiting blocca tempestivamente gli attacchi

Gli aggressori puntano su un alto Frequenze delle richieste, per sfruttare in modo improprio i moduli di accesso, sovraccaricare le API o estrarre contenuti in modo automatizzato. Limito quindi le richieste per chiave – solitamente per IP – e decido se limitarle, ritardarle o rispondere con un codice di stato 429. In questo modo tengo lontano il traffico dei bot dalla CPU, dal database e dalla logica applicativa, lasciando passare gli utenti legittimi. Ne traggono grande beneficio in particolare i percorsi sensibili come /login, /auth, /xmlrpc.php o le ricerche che richiedono molte risorse. La fonte di questa procedura è la Documentazione di Nginx sul modulo ngx_http_limit_req_module.

Come funziona il modulo NGINX nella pratica

Il modulo funziona secondo il Secchio che perdePrincipio: per ogni chiave, NGINX memorizza i valori di conteggio in una zona e li confronta con la frequenza consentita. Chiavi tipiche sono $binary_remote_addr per gli indirizzi IP, i token per le chiavi API o i valori derivati tramite map. Se un client supera in modo persistente la frequenza consentita e il buffer di burst, NGINX rifiuta la richiesta prima che raggiunga il backend. Ciò consente di risparmiare tempo di elaborazione e riduce la latenza per i visitatori reali. Impostiamo la risposta con 429 Too Many Requests o, a scelta, un altro Codice di stato um.

Configurazione: spiegata passo dopo passo

Comincio con una zona nella sezione http, imposto una frequenza moderata e la attivo in modo mirato sui percorsi sensibili. Per i picchi di breve durata definisco un «burst», opzionalmente con «nodelay», per evitare rifiuti bruschi. Successivamente eseguo dei test nell’ambiente di staging e analizzo i log prima di passare all’ambiente di produzione. In questo modo non rischio di bloccare inutilmente gli utenti reali. Un esempio sintetico illustra il Sintassi tangibile:

# 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;
  }
}

Protezione bot con zone e logica user-agent

I limiti basati sull'IP raramente sono sufficienti contro le botnet distribuite, pertanto suddivido il traffico in zone: Agli utenti vengono assegnati valori più generosi, mentre ai crawler generici valori più restrittivi. Con map valuto gli user-agent, riconosco i bot autorizzati come Googlebot e assegno loro limiti specifici e strettamente monitorati. Per gli scraper sconosciuti impongo limiti rigidi su percorsi che comportano costi elevati. Se rilevo degli schemi ricorrenti, aumento dinamicamente il livello di rigore fino a quando il Tasso è tornato nella norma.

Messa a punto: rate, burst, nodelay e codici di stato

Il parametro "Rate" regola la velocità di trasmissione al secondo, "Burst" consente picchi temporanei di bufferizzazione, mentre "nodelay" determina se preferisco la bufferizzazione o il passaggio immediato. Inizio con impostazioni moderate, ad esempio 10 richieste al secondo con burst 20 sulle API, e poi le affino in base all’analisi dei log. Per le route di login, ad esempio, imposto 1 richiesta al secondo con un burst ridotto, per frenare gli attacchi brute force. In caso di superamento dei limiti, restituisco un 429, perché i client in questo modo cavarsela e la logica di riprova funzioni correttamente. In casi particolari, utilizzo codici alternativi se i clienti lo richiedono.

Panoramica nella tabella: direttive e utilizzo

Il seguente Tabella riassume le linee guida fondamentali e illustra in quali casi è opportuno applicarle.

direttiva Effetto Esempio Utilizzo tipico
limit_req_zone Inserire la chiave, la zona e Tasso fermo limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s; Base per IP, token o user-agent
limit_req Attiva il limite in Posizione/Server limit_req zone=perip burst=20 nodelay; Controllo granulare per percorso o vHost
limit_req_status Aggiunge il codice HTTP Superamento limit_req_status 429; Comportamento corretto del client e tentativi di riconnessione
mappa Inoltra le richieste a zone all'indirizzo map $http_user_agent $is_bot {…} Distituzione tra bot e utenti in base all'User-Agent

Esempio pratico: proteggere in modo mirato l'endpoint di accesso

Limito l'accesso a /login in modo molto rigoroso, perché i bot provano password con un alto Frequenza Provare diverse impostazioni. 1 richiesta al secondo con burst 3 impedisce tentativi massicci di accesso senza penalizzare eccessivamente gli utenti reali. Inoltre, registro i tentativi falliti ripetuti nel log per bloccare temporaneamente gli indirizzi IP. In combinazione con l’autenticazione a due fattori (2FA) e, facoltativamente, il Captcha, la pressione sul database e sulla gestione delle sessioni si riduce notevolmente. In questo modo mantengo basso il numero di tentativi falliti e garantisco che il Accesso pronto e stabile.

Pratica: fornire le API in modo equo e controllato

Le API richiedono chiare Probabilità, in modo che i singoli client non monopolizzino l'intera larghezza di banda. Per i percorsi generici imposto 10 richieste al secondo (r/s) e un picco di 20; per gli endpoint più costosi, valori più restrittivi. Se sono disponibili token o chiavi API, impongo una limitazione per token anziché per IP. Ciò garantisce equità tra i clienti e previene gli abusi. Per un approfondimento, si veda la mia nota su Limitazione della frequenza delle chiamate API, che inquadra il concetto in una prospettiva più ampia.

Monitoraggio, registrazione dei dati e ottimizzazione iterativa

Registro 429 risposte, comprese Chiave (ad es. IP o token) e il percorso, per individuare eventuali schemi ricorrenti. I picchi su pochi percorsi indicano attività di scraping o attacchi di forza bruta; un carico distribuito fa pensare a botnet. Con questi dati, applico limiti solo dove è necessario e riduco al minimo i falsi positivi. Le dashboard con frequenze, tasso di errore e latenza mi mostrano l’effetto di ogni modifica. In questo modo, la Prestazioni elevato, mentre la protezione aumenta.

Inserimento in un approccio olistico alla protezione

Ritengo che il “rate limiting” sia un ottimo primo passo strato, ma lo abbino a regole WAF, reputazione IP e rafforzamento TLS. Contro gli attacchi di volume è utile una protezione DDoS a monte, che filtra il traffico a livello di rete prima che NGINX debba entrare in azione. Misuro costantemente le metriche, imposto avvisi in caso di picchi anomali e reagisco aggiornando le regole. In questo modo, i vari elementi si combinano per formare una rete di protezione resiliente. Questi articoli forniscono una panoramica pratica Strategie DDoS.

Modelli di configurazione specifici per i bot rispetto alle persone

Con map suddivido i visitatori in categorie e li indirizzo in pagine dedicate zone. Ai crawler noti vengono assegnati limiti moderati, mentre agli agenti generici vengono applicati limiti più severi. Per percorsi come /search o /report mantengo criteri più rigorosi, poiché richiedono un elevato utilizzo della CPU. In caso di violazioni ricorrenti, non aumento i limiti, ma applico un blocco temporaneo o trasferisco la verifica a un modulo di rilevamento dei bot. In questo modo la Tasso di uso improprio basso, senza interferire con i motori di ricerca.

Esempio: due zone e mappatura dell'user-agent

Il seguente frammento mostra la suddivisione in base a Agente utente e l'assegnazione di limiti adeguati. Combino tutto ciò con codici di stato differenziati e campi di log per misurare con precisione l'efficacia. I bot con un agente generico finiscono nella zona più restrittiva. Le persone o i crawler verificati utilizzano la zona più flessibile. Questo approccio garantisce risultati prevedibili Produttività per 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;
  }
}

Gestione degli errori: comunicare correttamente il codice 429

Su Limits offro una chiara Risposta indicando quando è opportuno effettuare un nuovo tentativo. Per le API, è necessario includere l’intestazione Retry-After valida, affinché i client applichino il backoff. Agli utenti umani viene fornita una breve spiegazione senza dettagli tecnici. Ciò riduce il numero di ticket e garantisce un comportamento comprensibile. Una soluzione pulita UX rende i limiti accettabili ed evita la frustrazione.

Host, rete e kernel: rafforzare le basi

Un elevato traffico legittimo e le misure di difesa richiedono soluzioni affidabili Risorse e impostazioni predefinite adeguate a livello di rete. Mi assicuro che le versioni di NGINX siano aggiornate, che ci sia RAM sufficiente per le zone e che siano attive le funzioni di protezione contro gli attacchi di tipo “transport”. Per contrastare gli attacchi SYN-flood è utile attivare Cookie TCP SYN nel kernel, in modo che le connessioni non rimangano bloccate. In definitiva, questo alleggerisce NGINX da un carico superfluo. In questo modo concentro i limiti sui livelli HTTP e mantengo il Produttività stabile.

In breve: ecco come utilizzo in modo efficace il Rate Limiting di NGINX

Limito le richieste in base a chiave, isolo i percorsi critici e tengo a distanza i bot grazie a zone rigorosamente definite. Le funzionalità Burst e nodelay aiutano a consentire picchi legittimi senza favorire gli abusi. Attraverso i log 429, calibro continuamente i valori e inasprisco i limiti solo dove è necessario. In combinazione con WAF, difesa DDoS, monitoraggio e hardening del kernel, si crea un concetto di protezione resiliente. Chi lo implementa in modo coerente riduce significativamente il traffico dei bot e preserva Prestazioni anche sotto carico.

Elementi che spesso mancano nella pratica

In molte configurazioni mancano alcuni componenti fondamentali che aumentano sensibilmente l'efficacia del rate limiting:

  • IP reali dei client dietro i proxy: Se la gestione dell’IP reale non è corretta, NGINX spesso limita l’IP del bilanciatore di carico; in tal caso, i limiti si applicano a tutti gli utenti raggruppati a valle.
  • Prove a secco (dry-run): I limiti vengono attivati „alla cieca“. È preferibile registrare in anticipo solo il numero di volte in cui un limite sarebbe scattato.
  • Chiavi a grana fine: Anziché limitarsi all’indirizzo IP, è opportuno applicare limitazioni per ogni token API, sessione o utente, al fine di garantire una maggiore equità.
  • Interazione con limit_conn: Le connessioni parallele e le frequenze delle richieste coprono diversi modelli di abuso.
  • Eccezioni mirate: Gli health check, i webhook o i servizi interni richiedono spesso limiti meno rigidi o addirittura nessun limite.

Proxy inverso: analizzare in modo sicuro l'IP reale del client

Se NGINX si trova dietro un bilanciatore di carico, imposto le direttive Real-IP in modo che $binary_remote_addr rifletta il client effettivo. Mi fido solo delle reti di mia proprietà e attivo la valutazione ricorsiva:

http {
  # Intervalli IP proxy affidabili (esempio)
  set_real_ip_from 10.0.0.0/8;
  set_real_ip_from 192.168.0.0/16;
  # Aggiungere eventualmente intervalli LB/CDN pubblici

  real_ip_header X-Forwarded-For;
  real_ip_recursive on;

  limit_req_zone $binary_remote_addr zone=perip:20m rate=10r/s;
}

Senza questa impostazione, un limite finirebbe altrimenti per interessare contemporaneamente molti utenti innocenti. Dopo la configurazione, controllo tramite i log di accesso se compare l’IP del client previsto.

Strategia chiave: IP, utente, token e percorso

La chiave scelta determina l'equità e l'efficacia. Alcuni modelli collaudati:

  • Pro IP ($binary_remote_addr): pronto all’uso, ideale per /login e endpoint anonimi.
  • Per ogni token API: Equità tra i clienti; protegge dal raggruppamento NAT. Estraggo i token tramite map.
  • Per ogni classe di percorso: Limitare separatamente gli endpoint più costosi, ad esempio /search in misura maggiore rispetto a /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/ {
    # Rilevole solo se è presente un token
    limit_req zone=per_token burst=10;
    limit_req_status 429;
  }
}

Importante: un’elevata cardinalità delle chiavi consuma memoria nella zona. Prevedere dei buffer e monitorare l’utilizzo della memoria.

Accumulo e dimensionamento delle zone

La zona memorizza i metadati per ogni chiave attiva. Il consumo per ogni voce è di alcune decine di byte più l'overhead. Da ciò deduco che:

  • Per un numero elevato di IP/token simultanei, scelgo zone più grandi, ad esempio da 50 a 100 MB.
  • Comincio con impostazioni piuttosto generose e leggo i log di NGINX: il messaggio „shared memory zone is full“ indica la necessità di una nuova ottimizzazione.
  • Le chiavi inutilizzate scadono dopo un breve periodo di inattività; i picchi sono più importanti della media giornaliera.

Utilizzare con precisione le funzioni "Burst" e "nodelay"

Senza nodelay NGINX raggruppa i superamenti all’interno del buffer di burst e ritardato Richieste. Con nodelay Le richieste in burst consentite vengono immediatamente accettate, mentre quelle in eccesso vengono respinte. Il mio approccio:

  • Percorsi interattivi (HTML): meglio evitare il tag `nodelay`, per generare brevi tempi di attesa anziché un errore 429 definitivo.
  • API: spesso con "nodelay", in modo che i client ricevano chiaramente il codice di errore 429 e applichino il backoff.
  • Terminali costosi: un breve burst per appianare i picchi del backend.

Prova generale, livello di log e analisi

Prima di rendere effettivi i limiti, attivo la modalità Dry-Run e regolo il livello di log. In questo modo posso vedere l'effetto senza correre rischi:

server {
  location /api/ {
    limit_req zone=perip burst=20;
    limit_req_dry_run on; # solo registrazione, nessun blocco
    limit_req_log_level notice;  # meno grave di 'error'
  }
}

Successivamente analizzo i dati di accesso relativi a un periodo di 3–7 giorni, individuo i punti critici, regolo la velocità e il burst e solo allora disattivo il Dry-Run.

429: trasporto corretto: HTML, JSON e Retry-After

Per garantire una buona esperienza utente (UX), distinguo tra browser e client API e impiego Retry-After. Ecco come comunico chiaramente i limiti:

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 "Si prega di riprovare più tardi.";
  }
}

Le API possono reagire in modo programmatico, mentre gli utenti ricevono un messaggio chiaro.

Combinare limit_req e limit_conn

limit_req portata per intervallo di tempo, limit_conn limita il numero di connessioni simultanee. Per contrastare i download, i client "chatty" o i picchi di traffico HTTP/2, combino entrambe le soluzioni:

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 connessioni simultanee per IP
  }
}

In questo modo evito che alcuni client, pur rispettando la velocità di trasmissione, occupino troppe risorse con un numero eccessivo di connessioni simultanee.

Eccezioni, controlli di integrità e percorsi interni

Non tutti i percorsi necessitano di limiti. Gli health check (/healthz), i webhook interni o i callback di pagamento hanno location dedicate senza limit_req – oppure con valori più moderati:

server {
  # nessun limite per i controlli di integrità
  location = /healthz { return 200 "ok"; }

  # limiti moderati per i callback di pagamento
  location /webhooks/pay/ {
    limit_req zone=perip burst=5;
  }

  # protezione rigorosa per il login
  location = /login {
    limit_req zone=perip rate=1r/s burst=3;
  }
}

Le eccezioni granulari riducono i falsi positivi e garantiscono la stabilità delle integrazioni.

Un routing a zone più solido senza la “magia” di If

Per la separazione „bot vs. utente“, preferisco utilizzare reindirizzamenti interni tramite “named locations”. Ciò rende la configurazione chiara e prevedibile:

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; }   # reindirizzamento interno
    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;
  }
}

In questo modo, i bot finiscono in modo deterministico nella zona rigorosa, mentre le persone in quella rilassata – senza che i due limiti agiscano contemporaneamente.

Provare, misurare, indossare: una procedura pragmatica

  • Messa in scena: Scegliere un’impostazione conservativa per Rate/Burst, attivare Dry-Run, eseguire il carico sintetico sul percorso critico.
  • Test del fumo: Generare brevi raffiche con curl o Lasttools e verificare il comportamento del codice 429/Delay.
  • Progetto pilota produttivo: Per prima cosa, applicare la misura a singole sedi e monitorare attentamente i log.
  • Affilatura iterativa: Applicare i limiti solo dove si notano degli schemi ricorrenti; ridurre al minimo i falsi allarmi.
Esempio #: test di burst veloce con curl
for i in {1..50}; do curl -s -o /dev/null -w "%{http_code}\n" https://example.com/login & done; wait

Frequenze in minuti anziché in secondi e percorsi granulari

NGINX consente di impostare intervalli in secondi o minuti (r/s, r/m). In caso di abuso di login, spesso imposto 60 richieste al minuto anziché 1 al secondo, per consentire brevi doppi clic legittimi ma limitare il fuoco continuo. Ai percorsi costosi vengono applicati limiti più restrittivi rispetto a quelli economici. Esempio:

limit_req_zone $binary_remote_addr zone=perip_min:20m rate=60r/m;

server {
  location /search/ {
    limit_req zone=perip_min burst=10;   # più rigoroso
  }
  location /status {
    # nessun limite – economico e utilizzato internamente
    return 200;
  }
}

Gli ostacoli e come evitarli

  • Chiave sbagliata: Quando utilizzo proxy senza indirizzo IP reale, finisco per limitare inavvertitamente tutti gli utenti contemporaneamente.
  • Zone troppo piccole: Il messaggio „zone is full“ comporta un comportamento imprevedibile – prevedere dimensioni generose.
  • Un limite per tutto: Percorsi diversi richiedono valori diversi; un approccio “universale” genera frustrazione.
  • Nessun monitoraggio: Senza l'analisi 429, le configurazioni errate passano inosservate.
  • Lista bianca estesa: Eccezioni troppo ampie aprono le porte a ogni sorta di abuso – occorre creare una lista bianca mirata, temporanea e trasparente.

Caratteristiche specifiche di HTTP/2, SSE e caching

HTTP/2 raggruppa le richieste su un numero ridotto di connessioni; limit_conn rimane comunque rilevante, poiché gli streaming consumano risorse. Gli eventi inviati dal server o i download di lunga durata raramente attivano i limiti di velocità (pochi request), ma richiedono tempo: in questi casi applico limitazioni in parallelo con `limit_conn` o implemento strategie di gestione della larghezza di banda. Ove possibile, alleggerisco il carico con Caching (ad es. risorse statiche, richieste GET frequenti), in modo che i limiti vengano applicati meno spesso e gli utenti ricevano risposte più rapide.

Lista di controllo operativa

  • IP reale corretto, chiave definita (IP/token/utente)
  • Zone di dimensioni generose, metriche/log disponibili
  • frequenza/burst regolata per classe di percorso, nodelay impostato intenzionalmente
  • Testato in modalità dry-run, implementata la comunicazione 429 (Retry-After)
  • Eccezioni per Health/Webhooks, combinazione con limit_conn
  • Ricalibrazione iterativa e segnalazione delle anomalie

Articoli attuali