Un limiteur de débit protège un serveur contre les robots trop gourmands. Mal réglé, il protège aussi le serveur contre Google — et le fait savoir de la pire façon, en se faisant passer pour une panne. Ce guide part d'un cas que nous avons diagnostiqué sur notre propre site de comparaison de prix du contrôle technique, puis généralise la méthode.
Le symptôme : des erreurs serveur que personne ne voit
Le relevé Search Console de septembre 2026 de Mon Contrôle Technique affichait 357 pages en « Erreur serveur (5xx) ». Or aucune erreur applicative ne les expliquait : pas d'exception dans les journaux PHP, pas de redémarrage de conteneur, pas de requête lente en base. Ouvertes dans un navigateur, ces pages répondaient normalement.
C'est le profil typique d'une erreur qui dépend du contexte de la requête et non de la page elle-même. Une page qui échoue toujours est un bug ; une page qui échoue seulement quand un robot la demande au milieu d'une série est un problème de débit.
La cause : limit_req répond 503 par défaut
Les URL concernées étaient toutes sous /controle-technique/, la zone qui porte les pages de villes et de centres — de loin les plus nombreuses du site. Ce bloc était protégé par une zone de limitation :
limit_req_zone $binary_remote_addr zone=ct_crawl:10m rate=3r/s;
location ^~ /controle-technique/ {
limit_req zone=ct_crawl burst=10 nodelay;
proxy_pass http://localhost:8084;
}
Trois requêtes par seconde et par adresse IP, avec une rafale tolérée de dix : c'est raisonnable pour un humain, qui ne charge jamais trente pages de villes en dix secondes. Mais la directive s'applique à toute adresse, Googlebot compris. Et lorsqu'une requête dépasse le quota, Nginx répond par défaut 503 Service Unavailable.
Le 503 dit « le service est indisponible ». Pour un robot, rien ne distingue ce refus volontaire d'un serveur réellement tombé. Search Console le range donc, logiquement, parmi les erreurs serveur.
La correction : dire la vraie raison du refus
Le code HTTP prévu pour ce cas existe depuis 2012 : 429 Too Many Requests, défini par la RFC 6585. Il signifie « tu demandes trop vite », ce qui est exactement la situation. Nginx permet de le choisir en deux directives, à placer au niveau http pour qu'elles valent pour toutes les zones :
limit_req_status 429;
limit_conn_status 429;
Point important : le quota ne change pas. Les mêmes requêtes sont refusées au même moment ; seule la réponse change. Ce n'est pas un assouplissement de la protection, c'est une correction de vocabulaire.
Ce que le 429 change, et ce qu'il ne change pas
Il faut être précis ici, car beaucoup d'articles promettent plus que ce que Google documente. D'après la documentation officielle sur les codes HTTP, Google traite les 429 comme les erreurs 5xx du point de vue du rythme d'exploration : dans les deux cas, ses robots ralentissent temporairement. Et si les refus durent, les URL déjà indexées peuvent finir par en sortir. Le 429 n'est donc pas un passe-droit.
Ce qu'il apporte est ailleurs :
- Un diagnostic lisible. Dans vos journaux comme dans vos outils de surveillance, un 429 se sépare immédiatement d'un 503 applicatif. Tant que le limiteur répondait 503, une vraie panne PHP aurait été noyée dans les refus volontaires — et inversement.
- Une sémantique juste pour les clients qui la respectent. Un robot bien conçu sait qu'un 429 appelle un ralentissement, pas un signalement d'incident.
- Des rapports qui ne mentent plus. Le poste « Erreur serveur (5xx) » de Search Console redevient un indicateur de santé applicative, au lieu d'un mélange de pannes et de quotas.
Au moment où nous écrivons, la correction est en production depuis quelques jours ; l'évolution du poste dans Search Console demande plusieurs semaines pour être lisible. Si le poste ne reflue pas, la cause sera à chercher ailleurs, côté PHP. C'est la seule conclusion honnête à ce stade.
Vérifier un limiteur en production
Lire la configuration ne prouve rien : un fichier peut ne pas être inclus, une zone peut être écrasée par une autre location, un rechargement peut avoir échoué silencieusement. La seule preuve est un test contre le serveur réel. Le nôtre envoie une rafale de requêtes parallèles depuis une même adresse et compte les statuts :
URL="https://exemple.fr/controle-technique/une-page.htm"
seq 24 | xargs -P 24 -I{} curl -s -o /dev/null -w '%{http_code}\n' "$URL" \
| sort | uniq -c
Après déploiement, le résultat était de 12 réponses 200 et 12 réponses 429, aucune 503, puis un retour immédiat à 200 une fois la rafale passée. Le compte est cohérent avec la configuration : une requête au rythme nominal, dix absorbées par le burst en mode nodelay, et le reste refusé.
Deux précautions. D'abord, ce test est intrusif : il déclenche volontairement le limiteur et peut vous faire bannir si une protection de type fail2ban surveille les 429. Nous l'avons isolé dans une section à part de notre script de vérification, jamais exécutée par défaut. Ensuite, si un cache se trouve devant la zone testée, choisissez une URL qui n'est pas en cache, sinon vous mesurez le cache et non le limiteur.
Comment dimensionner le quota
Le statut HTTP réglé, reste la vraie question : pourquoi Google atteignait-il le quota ? Trois leviers, par ordre d'efficacité :
1. Mettre un cache devant les pages lourdes
Un limiteur protège le serveur d'applications. Si les pages visées sont servies depuis un cache, elles ne coûtent presque rien et la limite peut être beaucoup plus large. Dans notre cas, le bloc /controle-technique/ était le seul du site sans cache de proxy, alors qu'il porte les pages les plus nombreuses : chaque visite de Googlebot y sollicitait PHP. Ces pages affichent des prix, d'où une décision à prendre sur la durée de fraîcheur acceptable avant d'activer un cache. Le sujet est traité dans notre guide sur le cache Nginx devant WordPress.
2. Dimensionner à partir des journaux
Extrayez des journaux d'accès le nombre de requêtes par seconde et par adresse sur la zone, pour les visiteurs humains. Le quota doit se situer confortablement au-dessus de leur pic, et le burst doit absorber le chargement d'une page avec ses appels annexes (requêtes AJAX, API internes). Un réglage de tutoriel appliqué sans mesure est la cause la plus fréquente de ce type d'incident.
3. Traiter les robots vérifiés à part — avec prudence
Il est possible de créer une zone distincte pour les robots des moteurs, mais jamais sur la foi de l'user-agent, trivial à usurper : c'est précisément ce que font les aspirateurs de contenu. Une exemption sérieuse s'appuie sur une vérification par DNS inverse, ou sur les listes d'adresses publiées par Google. C'est plus lourd à maintenir ; nous ne l'avons pas mis en place, les deux premiers leviers étant suffisants à notre échelle.
Les autres usages du limiteur, qui eux doivent rester stricts
Le même mécanisme sert à des protections qui n'ont rien à voir avec l'exploration, et qui doivent rester sévères : la page de connexion de WordPress (wp-login.php), l'API REST, ou l'écran de connexion d'une interface d'administration. Sur nos serveurs, la connexion WordPress est limitée à une requête par seconde avec une rafale de cinq, et la connexion à l'administration Laravel à cinq tentatives par minute. Aucun moteur de recherche n'a de raison d'y aller : ces zones peuvent être strictes sans le moindre coût pour le référencement.
Le bon réflexe est donc de raisonner zone par zone : strict là où seuls des humains authentifiés ou des attaquants passent, dimensionné et adossé à un cache là où les moteurs doivent pouvoir explorer.
Méthode résumée
- Dans Search Console, repérez les erreurs 5xx sans contrepartie dans les journaux applicatifs.
- Croisez leurs URL avec les zones
limit_reqde votre configuration. - Passez
limit_req_statusetlimit_conn_statusà 429 au niveauhttp. - Vérifiez en production par une rafale contrôlée, sur une URL hors cache.
- Dimensionnez ensuite le quota d'après les journaux et placez un cache devant les pages lourdes.
- Suivez le poste « Erreur serveur » sur plusieurs semaines avant de conclure.
Pour lire correctement ces rapports, voir aussi notre guide sur le rapport d'indexation de Search Console.