2026.06.11 (Jeu)

✨ Résumé de GPT-5.5  

Retour sur la réduction du trafic de crawlers cloud et de clients headless légers dans le comptage Cloudflare Worker, avec filtres ASN/organisation, signaux de visible engagement et déblocage d’un ASN de FAI bloqué par erreur.

Les chiffres analytics semblaient encore étranges.

Après l’ajout de la page publique de statistiques, mon travail local ne polluait plus le compteur de production. Mais un autre problème est apparu.

Certaines requĂŞtes ne ressemblaient pas Ă  une lecture humaine.

Elles ouvraient plusieurs chemins très vite, utilisaient un User-Agent qui ressemblait à un navigateur, mais ne se comportaient pas comme un lecteur restant sur une page. Sur un blog statique public, les crawlers sont normaux. Le problème, c’est que s’ils touchent aussi /track, ils gonflent les vues et les visites publiques.

Quand j’avais ajouté le compteur, j’avais écrit que les bots et les visites en double étaient suffisamment bloqués. En production, ce « suffisamment » devait être relevé.

Le User-Agent ne suffisait pas

Le Worker avait déjà des contrôles de base.

bot user-agent
Cloudflare verified bot
Cloudflare bot score
dedupe window

Cela attrape les bots évidents.

Mais tous les crawlers n’écrivent pas bot dans leur User-Agent. Certaines requêtes ressemblent à Chrome. Le User-Agent seul ne distingue pas correctement une personne d’un navigateur automatisé léger.

Les données request.cf de Cloudflare contiennent ASN et organisation ASN. J’ai donc fait vérifier ces champs par /track.

TRACK_BLOCKED_ASNS
TRACK_BLOCKED_AS_ORGS

Les variables d’environnement ne remplacent pas entièrement les defaults. Elles s’ajoutent aux defaults du Worker. Les axes clairement bloqués restent dans le code, et les découvertes d’exploitation peuvent être ajoutées par environnement.

La comparaison exacte du nom d’organisation était trop faible.

Huawei Cloud
Huawei-Cloud-HK
Huawei Cloud Singapore POP
Huawei Clouds Singapore

Les noms varient ainsi. La comparaison accepte donc à la fois l’égalité exacte et le texte contenu. Un seul default peut couvrir plusieurs variantes.

Ajouter 3 secondes de visible engagement

Un filtre ASN seul est trop centré sur le réseau.

Un lecteur réel peut être derrière un FAI ou un réseau d’entreprise. Un crawler peut aussi venir d’un réseau ordinaire. Il fallait donc regarder des signaux côté client.

Désormais, le client n’envoie /track qu’après que la page est restée visible un court moment.

track_delay_seconds = 3

Trois secondes, ce n’est pas long. Un vrai lecteur ne le sent presque pas. Mais cela filtre des previews instantanées, du scraping en onglet caché, et des chargements qui disparaissent tout de suite.

Le client envoie ces valeurs dans le payload.

engagementMs
visibilityState
documentHidden
viewportWidth
viewportHeight

Le Worker les valide Ă  nouveau.

Il ne suffit pas de faire confiance au JS. Si engagementMs est inférieur au seuil, la requête est ignorée comme client_visible_too_short. Si le viewport est nul ou invalide, elle est ignorée comme client_viewport_invalid.

Les signaux visibles n’étaient pas optionnels

La première implémentation restait trop souple.

Elle vérifiait visibilityState et documentHidden quand ils existaient, mais une requête sans ces champs pouvait encore passer. Cela durcissait le nouveau client, mais laissait passer des appels manuels à /track sans signaux.

La condition du Worker est donc devenue stricte.

visibilityState === "visible"
documentHidden === false

Si ces deux conditions ne sont pas exactement satisfaites, la requête est ignorée comme client_signal_missing.

L’idée est de traiter hidden et missing comme la même famille. /track est l’endpoint qui augmente les compteurs publics. Une requête vers cet endpoint doit ressembler à une requête envoyée par la page normale et le client normal. Sans signaux, elle ne doit pas passer.

/analytics reste lisible publiquement, mais /track est plus exigeant.

Lire les statistiques
-> public

Augmenter les vues
-> production origin
-> visible engagement
-> viewport valide
-> filtres bot/ASN/org passés

Les valeurs bloquées doivent être revues

Ce travail a aussi montré qu’une blocklist ne s’améliore pas simplement en grandissant.

En voulant bloquer des crawlers cloud, on peut bloquer par erreur un ASN de FAI réel. Cela supprime aussi des lecteurs réels. J’ai donc retiré de la liste intégrée l’ASN ajouté par erreur.

Les filtres forts semblent rassurants, mais les false positives ont un coût.

Compter les bots gonfle le nombre.
Bloquer des personnes efface de vrais lecteurs.

Les deux sont mauvais.

Le critère est donc devenu ceci.

Bloquer dans le Worker les axes clairement cloud/crawler.
Exiger les signaux visibles du client.
Ne pas mettre du trafic de type FAI humain dans les blocages intégrés.
Garder les valeurs suspectes avec ignored_reason pour audit.

analytics_events conserve des lignes minimales avec ignored_reason même pour les requêtes ignorées. Sans cela, il devient difficile d’expliquer pourquoi un nombre a baissé ou ce qui a été bloqué.

Les chiffres doivent apparaître vite, mais pas trop facilement

Un compteur de visites repose sur un équilibre inconfortable.

S’il se met à jour trop lentement, il semble cassé. C’est pourquoi le travail sur le compteur utilisait un baseline GA plus des incréments immédiats en D1.

Mais si compter est trop facile, les bots comptent aussi.

Ce changement déplace un peu l’équilibre vers la prudence. Les lecteurs normaux comptent toujours après 3 secondes. Mais l’automatisation instantanée, les documents cachés, les viewports invalides et les axes cloud connus entrent plus difficilement.

Les statistiques publiques ne sont pas une comptabilité parfaite.

Mais elles doivent au moins ressembler à une trace de lecture humaine. Les grands nombres ne sont pas l’objectif. Les nombres fiables rendent la décision suivante possible.

Ce que j’ai vérifié

J’ai vérifié ceci pendant le travail.

node --check cloudflare/ga-stats-worker.js
node --check assets/js/custom/visitor-stats.js
git diff --check
bundle exec jekyll build
Cloudflare Worker deploy
GitHub Pages deploy

J’ai aussi fait des smoke tests du Worker.

/analytics?range=today
-> trackDelaySeconds: 3

/track without visibilityState
-> ignored, client_signal_missing

/track with visibilityState="hidden"
-> ignored, client_signal_missing

/track with documentHidden=true
-> ignored, client_signal_missing

J’ai aussi confirmé que le HTML déployé de la page d’accueil contenait data-track-delay-seconds="3".

Ce n’était pas une modification spectaculaire.

Mais les chiffres publics ont besoin de ce genre de défense. Les vues sont visibles, donc un mauvais comptage casse vite la confiance. Ce travail n’a pas rendu le compteur plus grand. Il l’a rendu plus difficile à tromper.

Laisser un commentaire