WatchTrail tourne sur Vercel, et deux fois en deux mois j'ai découvert une facture que je n'avais pas vue venir. Une fois par ma faute, une fois par celle d'un robot. Les deux avaient la même signature : aucune page ne tombait en erreur, tous les indicateurs étaient au vert, et pourtant le compteur montait. Voici les deux, avec les chiffres réels, parce que c'est le genre de chose qu'on ne trouve nulle part avant de la vivre.
Août : 58 Go d'écritures de cache pour des pages que personne ne lisait
Le site a une page par épisode de série. Environ 250 séries, une cinquantaine d'épisodes chacune, quatre langues : à peu près 50 000 adresses. Ces pages sont pauvres, une centaine de mots, et sont en `noindex` : elles n'existent que pour le confort de navigation.
En juillet, j'ai activé la pré-génération de pages avec `generateStaticParams`. L'intention était bonne : moins de calcul au moment de la visite. Ce que je n'avais pas compris, c'est que sur Vercel, chaque adresse pré-générée est aussi STOCKÉE, et qu'une écriture de cache se facture. Pas le rendu : l'écriture.
Le relevé du cycle a été sans appel : 7,21 millions d'écritures de 8 Ko, soit environ 58 Go, et 34,60 $ — contre un centime de lectures. Le rapport entre les deux disait tout : on écrivait un cache que personne ne relisait jamais. En parallèle, ces pages d'épisodes représentaient 251 772 rendus en 24 heures, 87 % de toute l'activité du serveur, pour des pages invisibles des moteurs.
Le correctif tient en une ligne : retirer `generateStaticParams` de cette route. Plus aucune entrée par adresse dans le manifeste, donc plus aucune écriture. Vérifié sous charge le lendemain : zéro écriture pendant un balayage à 350 requêtes par minute, contre environ 5 000 écritures par minute la veille.
Ce que j'en retiens : le coût d'une page ne se lit pas dans le nombre de visites. Il se lit dans ce que l'hébergeur écrit pour elle. Et un commentaire dans le code affirmait que cette route était dynamique — il mentait, personne ne l'avait vérifié depuis des semaines.
Septembre : un robot a lu 40 000 fiches en neuf heures
Le 13 septembre, entre 6 heures et 15 heures, le compteur de calcul est passé de deux minutes par jour à trente-neuf. Le transfert, de 35 Mo à 1,81 Go. Les journaux ne montraient rien d'anormal : aucune erreur, que des réponses normales.
Le détail par route désignait le coupable : 28 383 requêtes sur les fiches de films, 11 266 sur les fiches de séries, 24 739 adresses distinctes, presque toutes en anglais. Le nom du visiteur ne figure nulle part dans les journaux d'exécution — il a fallu ouvrir le pare-feu de l'hébergeur pour le lire : `GPTBot/1.4`, le robot d'entraînement d'OpenAI, 39 800 requêtes depuis une seule adresse.
Le mécanisme est le plus bête qui soit. Chaque fiche affiche une section « Dans le même genre », qui pointe vers d'autres fiches. Le catalogue derrière vient d'une base de données cinéma qui contient des centaines de milliers de titres. Vu depuis une fiche, ce catalogue est infini : un robot qui suit les liens ne s'arrête jamais, et chaque adresse inconnue produit une page complète servie en 200.
Ce n'est pas une attaque, et le robot n'a enfreint aucune règle : il était explicitement autorisé par notre `robots.txt`, un choix assumé pour être cité par les assistants. Le piège, c'est nous qui l'avions posé.
Le remède tient en deux morceaux. Le fichier `robots.txt` retire les fiches aux robots d'entraînement, en gardant tout ce qui vaut la peine d'être appris : l'accueil, les guides, les comparatifs, le blog. Et surtout, parce qu'un robot pressé ne lit pas toujours ce fichier, le serveur répond désormais 404, sans rendu, à tout robot qui n'est ni un moteur de recherche, ni un aperçu de partage, ni une requête déclenchée par un humain, dès qu'il demande une fiche absente de notre cache local — c'est-à-dire une fiche que personne chez nous ne suit.
Coût de la journée : environ 0,35 $. Sur un plan payant, c'est absorbé. Sur le palier gratuit, trente-neuf minutes de calcul par jour consomment le quota mensuel en une semaine, et le site s'éteint. C'est exactement ce qui nous était arrivé en août.
Ce que les deux ont en commun
- Aucune erreur. Dans les deux cas, zéro page en échec, zéro alerte. Une surveillance qui regarde les erreurs ne voit rien.
- Le coût ne vient pas du trafic humain. Il vient de ce que l'hébergeur écrit, et de ce que les robots lisent. Deux choses qu'aucun outil de mesure d'audience ne montre.
- La cause était un choix technique raisonnable, pris pour une bonne raison, dont l'effet sur la facture n'était écrit nulle part.
- Le diagnostic a demandé de sortir des journaux d'exécution. Le premier s'est lu dans le relevé de facturation, le second dans le pare-feu. Aucun des deux n'était visible côté application.
Si vous hébergez un site qui génère des pages à partir d'un catalogue extérieur, deux questions valent d'être posées aujourd'hui plutôt qu'à la prochaine facture. Est-ce que mes pages pré-générées sont relues au moins une fois chacune ? Et est-ce qu'un robot qui suit mes liens internes finit par s'arrêter ?