Aller au contenu
EuroVPSEuroVPS

Limitez le chemin coûteux, pas le crawler

Trouvez les URL qui coûtent du vrai travail et limitez-y les crawlers avec les stick tables HAProxy et un 429, sans nuire à la recherche ni aux visiteurs.

Par EuroVPS Engineering

5 min de lecture
Limitez le chemin coûteux, pas le crawler

Bloquer les bots est facile et généralement une erreur. Les moteurs de recherche vous amènent des visiteurs, les aperçus de liens vous amènent des clics, et même les crawlers IA agressifs sont souvent un trafic que vous préférez ralentir plutôt qu'exclure. La meilleure approche est de trouver où les crawlers vous coûtent vraiment de l'argent, c'est-à-dire une poignée de chemins hors cache et lourds pour la base de données, et d'y contrôler le débit, au niveau du load balancer. Voici comment nous le faisons.

Savoir ce qui est coûteux avant d'écrire une règle

Le coût vit sur les chemins qui ratent le cache et atteignent PHP et la base de données : les sitemaps générés à la volée, la recherche, la navigation à facettes où chaque combinaison de filtres est une requête fraîche, et tout ce qui porte une query string que le cache ne connaît pas.

Votre access log vous dit déjà lesquels. Si le log enregistre le temps de requête (le $request_time de nginx, par exemple, comme dernier champ), additionnez le temps passé par chemin :

awk '{split($7, p, "?"); t[p[1]] += $NF; n[p[1]]++}
     END {for (u in t) printf "%10.1f s %8d req  %s\n", t[u], n[u], u}' access.log \
  | sort -rn | head -20

Puis regardez qui le dépense, par user agent, sur ces chemins :

awk -F'"' '$2 ~ /sitemap|search|filter=/ {print $6}' access.log \
  | sort | uniq -c | sort -rn | head -20

Deux listes, le travail d'une soirée, et vous savez exactement où une règle sera rentable.

Limiter le débit avec les stick tables HAProxy

HAProxy peut compter les débits de requêtes par client et par user agent en mémoire, à la vitesse du lien, et agir dessus. Une configuration minimale qui ne protège que les chemins coûteux :

backend st_src
    stick-table type ipv6 size 200k expire 10m store http_req_rate(10s)

backend st_ua
    stick-table type string len 128 size 20k expire 10m store http_req_rate(10s)

frontend fe_web
    # bind lines and certificates as usual
    acl expensive path_beg /sitemap /search
    acl expensive url_sub -i filter=
    http-request track-sc0 src table st_src if expensive
    http-request track-sc1 req.fhdr(user-agent) table st_ua if expensive
    http-request deny deny_status 429 hdr Retry-After 60 if expensive { sc_http_req_rate(0) gt 20 }
    http-request deny deny_status 429 hdr Retry-After 60 if expensive { sc_http_req_rate(1) gt 60 }

Trois choix de conception comptent ici. Seuls les chemins coûteux sont suivis, donc rien d'autre sur le site n'est touché. La réponse est un 429 avec un header Retry-After, que les crawlers bien élevés comprennent comme « ralentis et reviens », pas comme une erreur pour laquelle vous pénaliser. Et la seconde table compte par user agent, ce qui attrape un crawler réparti sur de nombreuses adresses qui passerait sous une limite par adresse. Les seuils ci-dessus sont indicatifs : fixez les vôtres à partir de ce que le chemin peut réellement servir hors cache.

Identifier les crawlers avec soin

Faire correspondre les user agents de façon souple

Les crawlers changent le format de leurs chaînes user agent, en ajoutant des jetons de version, des URL ou de nouveaux noms de produits. Une règle qui fait correspondre toute la chaîne exactement cesse de fonctionner le jour où elle change. La correspondance par sous-chaîne sur la partie stable (hdr_sub(user-agent) -i <name>) continue de fonctionner.

Ne pas bloquer le moteur de recherche par accident

Vérifiez un crawler avant de le traiter différemment. Les grands moteurs de recherche publient comment : une résolution DNS inverse sur l'adresse du client doit renvoyer un hostname dans leur domaine, et une résolution directe sur ce hostname doit renvoyer la même adresse.

host <client-address>        # should return a name in the engine's published domain
host <name-returned-above>   # should return the same client address

Les crawlers authentiques sont alors limités sur les chemins coûteux comme tout le monde, et continuent de parcourir librement le reste du site. Les imposteurs n'ont droit à aucune faveur.

Rejeter les formes qui ne sont jamais légitimes

Certains motifs de requêtes n'ont aucun usage légitime : des URL réencodées encore et encore jusqu'à faire des milliers de caractères, des combinaisons de paramètres impossibles, le même percent-encoding appliqué trois fois. Une seule règle en edge les rejette avant qu'elles coûtent quoi que ce soit :

http-request deny deny_status 400 if { url_sub -i %2525 }

Adaptez le motif à ce que montrent vos logs. L'essentiel est que le load balancer est l'endroit le moins cher de la stack pour dire non.

Protéger PHP de ce qui passe

Aucune règle en edge n'est parfaite, donc la couche applicative a besoin de ses propres limites. Dans chaque pool PHP-FPM, un délai de terminaison empêche une requête pathologique de retenir un worker pendant des minutes, et un plafond mémoire l'empêche de grossir sans limite :

request_terminate_timeout = 60s
php_admin_value[memory_limit] = 256M
pm.max_requests = 500

Réglez-les avec les règles en edge, pour que les deux couches s'accordent sur ce à quoi ressemble une requête raisonnable. Pendant ce temps, Varnish continue de servir les pages en cache, donc lecteurs et acheteurs ne ressentent rien de tout cela.

Comment nous le faisons tourner

Sur les sites de presse et d'e-commerce que nous exploitons, les chemins de sitemap, de recherche et de listes filtrées reçoivent des gardes de débit dès le premier jour, dimensionnées à ce que chaque site peut servir hors cache. Les nouvelles règles sont écrites à partir des access logs, déployées sur chaque edge du site, et consignées avec les chiffres avant et après pour le client. Quand un nouveau crawler apparaît, une règle ciblée peut en général être en place sur chaque edge quelques minutes après l'identification du motif.

Vérifiez votre propre edge

  • Quels cinq chemins ont coûté le plus de temps PHP et base de données la semaine dernière ?
  • Qui les a demandés, par user agent ?
  • Vos limites s'appliquent-elles seulement là où est le coût, ou à tout le site ?
  • Répondez-vous par un 429 et Retry-After, ou par un blocage ?
  • Quand quelqu'un a-t-il confirmé pour la dernière fois que les règles correspondent encore au trafic réel ?

Si les crawlers mangent votre capacité, nous pouvons lire vos logs avec vous et écrire les règles. Parler à un ingénieur

Related

  • /what-we-do/operations/edge-traffic-control/
  • /what-we-do/data-web/high-availability-web-tiers/
  • /what-we-do/data-web/performance-audits/

Publié le 12 octobre 2026.

Toutes les notes de terrain