Come appare il messaggio
HTTP/2 429
retry-after: 30
203.0.113.5 - - [29/Oct/2026:10:22:41 +0000] "GET /api/items HTTP/2.0" 429 169 "-" "python-requests"Cause frequenti
- Un client o uno script invia richieste troppo in fretta, senza back-off o retry, e supera una quota.
- Un rate limit in nginx (
limit_reqconlimit_req_status 429), fail2ban, un WAF o il CDN è più severo del traffico reale. - Molti utenti condividono un IP (ufficio, rete mobile, proxy) e vengono contati come un solo client, oppure dietro un proxy non viene ripristinato l'IP reale del visitatore.
- Bot, crawler o una scansione di vulnerabilità martellano il sito.
- Un'API remota (il 429 arriva da terzi) ha esaurito la sua quota.
Verifica
- Chi riceve 429 nell'access log (lo stato è il campo 9 nel formato predefinito)?
awk '$9 == 429 {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head - Quali limiti sono configurati?
nginx -T 2>/dev/null | grep -nE 'limit_req|limit_conn|limit_req_status' - Cosa dice la risposta sul limite?
curl -sI https://example.com/ | grep -iE '^(HTTP|retry-after|x-ratelimit)' - L'IP del client nel log è l'IP reale del visitatore?
nginx -T 2>/dev/null | grep -nE 'real_ip|set_real_ip_from'
Soluzione
- Individua prima il livello: l'access log (server proprio), gli header della risposta (CDN o API) e lo stato di fail2ban dicono chi risponde.
- Per un limite tuo, regolalo: per esempio
limit_req zone=perip burst=20 nodelay;con una rate adatta all'uso normale. - Dietro un proxy ripristina l'IP reale del client (
real_ip_header,set_real_ip_from) così gli utenti non vengono contati insieme. - Escludi dal limite i client fidati con
geoemapo una lista di autorizzati e blocca a parte i bot abusivi. - Come client: rispetta
Retry-After, usa un back-off esponenziale, metti in cache le risposte e raggruppa le richieste.
Domande frequenti
limit_req di nginx restituisce 429 per impostazione predefinita?
No, lo stato predefinito è 503. Imposta limit_req_status 429; se vuoi il codice standard.
Devo riprovare subito?
No. Attendi almeno il tempo indicato in Retry-After, se presente, e aumenta l'attesa a ogni tentativo, altrimenti resti limitato.