[🛠] Créer une page publique de statistiques du blog
✨ Résumé de GPT-5.5  
Un retour sur l’enquête autour d’un compteur de visites anormalement élevé, le blocage du trafic local dans le comptage, et l’ajout d’une page publique d’analytics agrégés avec Cloudflare Worker et D1.
Le compteur de visites semblait faux.
Le nombre quotidien a soudain dépassé 80.
J’aurais pu penser à du trafic de recherche. Mais quelque chose clochait. Depuis plusieurs jours, je modifiais beaucoup le blog et, pendant le travail avec l’IA, j’avais ouvert plusieurs fois le serveur local et l’URL publique.
Il fallait donc poser la question.
Était-ce du vrai trafic ?
Ou mon propre travail était-il compté aussi ?
Le compteur était trop honnête
J’ai d’abord revu la structure.
Le compteur créé dans /fr/devlog/github-pages-blog/github-pages-blog-visitor-counter/ utilise Cloudflare Worker et D1.
Quand une page se charge, JavaScript envoie /track au Worker. Le Worker incrémente counters, page_counters et dedupe_views dans D1. Google Analytics sert seulement de baseline ; les nouveaux incréments vont dans D1.
Le problème était que cette structure était trop honnête.
Si un navigateur ouvrait une page, elle était comptée.
Le serveur local pointait lui aussi vers le Worker de production. Les origins autorisées du Worker incluaient localhost:4000 et 127.0.0.1:4000.
Donc une prévisualisation locale pouvait augmenter le vrai compteur D1 de production.
La déduplication était aussi basée sur visitorId + path. La même page dans le même navigateur est dédupliquée pendant 10 minutes, mais ouvrir plusieurs articles différents compte chaque article.
D1 l’a montré clairement.
day:2026-06-06 = 14
day:2026-06-07 = 38
day:2026-06-08 = 81
day:2026-06-09 = 5
Le 2026-06-08, beaucoup d’anciens Daily Review et de posts naver-* avaient été touchés une fois. Cela ressemblait davantage à une session de vérification qu’à de vrais lecteurs.
Le local doit afficher, pas compter
La première correction était simple.
En local, les statistiques peuvent s’afficher, mais /track ne doit pas être envoyé.
localhost
127.0.0.1
::1
Dans ces environnements, le script client saute le tracking.
Mais faire confiance au client seul est fragile. Si le JS change ou si une requête manuelle arrive, le problème revient. Le Worker compte donc /track uniquement depuis l’origin de production.
TRACK_ALLOWED_ORIGIN=https://hyuk.blog
Les requêtes venant d’origins locales sont maintenant ignorées avec origin_not_tracked.
J’ai aussi ajouté un opt-out pour vérifier la production sans polluer le compteur.
?hyuk_no_track=1
?visitor_tracking=off
?visitor_tracking=on
Ce n’est pas une administration cachée. C’est seulement un interrupteur pour inspecter la page publique sans modifier les chiffres.
Les statistiques n’avaient pas besoin d’être privées
Au début, j’ai envisagé une page d’analytics privée.
Mais ce n’était pas nécessaire.
Si je ne stocke pas les IP brutes, les User-Agent bruts, les full referrer URLs ni les logs d’événements individuels, la page peut être publique. Il suffit de stocker des valeurs agrégées affichables.
La direction a donc changé.
Pas une page privée de logs.
Une page publique de statistiques du blog.
/analytics/
Je l’ai ajoutée à la navigation principale et au petit bloc de visites dans la sidebar.
D1 ne garde que des agrégats
La nouvelle table est simple.
analytics_daily_dimensions
date
dimension
value
count
updated_at
Quand une page view est réellement comptée, le Worker classe les dimensions publiques et les agrège par jour.
Les dimensions sont :
page
content_group
locale
country
region
continent
colo
asn_org
device
viewport
browser
os
language
client_timezone
color_scheme
connection
traffic_source
referrer_domain
hour
weekday
Le point important est ce qui n’est pas stocké.
Le referrer est stocké comme domaine, pas comme URL complète. Le User-Agent est classé en browser, OS et device. L’IP n’est pas stockée.
Ce n’est pas un entrepôt analytique strict. C’est un tableau de flux public pour un blog personnel.
Aujourd’hui, mois, total et période personnalisée
Au début, je pensais seulement à aujourd'hui / mois / total, comme le compteur existant.
Mais une page de statistiques doit permettre des périodes.
L’API /analytics accepte :
/analytics?range=today
/analytics?range=month
/analytics?range=total
/analytics?range=custom&start=2026-06-09&end=2026-06-12
Ici, total signifie le total depuis le début de collecte de analytics_daily_dimensions, le 2026-06-09. Ce n’est pas le total de la sidebar, qui inclut encore le baseline GA.
Je n’ai pas mélangé ces deux sens.
D’abord utilisable sur mobile
Les pages de statistiques cassent vite sur mobile.
Un grand tableau devient immédiatement pénible. J’ai donc utilisé des cartes avec listes à barres.
En haut, il n’y a que quatre chiffres.
Période sélectionnée
Aujourd'hui
Ce mois-ci
Total
En dessous, chaque dimension a sa carte.
Sur mobile, les boutons de période passent sur deux lignes, les KPI en deux colonnes et les cartes détaillées en une colonne. Le formulaire de période personnalisée s’empile verticalement.
Sur desktop, les KPI restent sur une ligne et les cartes s’étalent en deux colonnes.
J’ai vérifié dans le navigateur. Pas d’overflow horizontal, pas d’erreur console.
Ce qui a été vérifié
Les vérifications :
node --check cloudflare/ga-stats-worker.js
node --check assets/js/custom/visitor-stats.js
node --check assets/js/custom/analytics-dashboard.js
bundle exec jekyll build
npx wrangler d1 execute hyuk-blog-view-counter --remote --file cloudflare/schema.sql
npx wrangler deploy --keep-vars
Après le déploiement, /analytics?range=today a répondu normalement.
La nouvelle table agrégée était encore vide. C’est attendu. Les analytics détaillés commencent à se remplir avec les vraies visites publiques après le déploiement.
J’ai aussi testé le tracking depuis une origin locale.
{"status":"ignored","reason":"origin_not_tracked"}
La vérification locale du blog n’incrémente donc plus le compteur de production.
Du compteur de visites aux analytics publics
Au début, il n’y avait que trois petits chiffres.
Aujourd’hui, mois, total.
Mais en l’utilisant vraiment, le plus important n’était pas le chiffre lui-même. C’était que le chiffre ne soit pas pollué. Et pour un blog public, afficher un flux agrégé sûr correspond mieux qu’une page privée de logs.
Ce n’est pas une comptabilité exacte.
Mais cela répond maintenant à des questions utiles.
Quels posts sont lus ?
De quels pays viennent les visites ?
Mobile ou desktop ?
Recherche, direct ou referral ?
Et surtout, quel bruit venant de mon propre travail faut-il exclure ?
C’est le rôle de /analytics/.
Laisser un commentaire