Analytics PHP sans cookies ni base de données — et sans violer le RGPD

Je voulais savoir quelles pages de mon portfolio sont consultées, d'où viennent les visites, et si quelqu'un lit le blog — sans brancher Google Analytics, sans installer une BDD, sans afficher un bandeau cookies. La contrainte : hébergement Apache + PHP pur, rien d'autre.

Pourquoi pas Google Analytics

GA4 aurait pris 5 minutes. Mais deux problèmes concrets :

  • RGPD : dès qu'on utilise GA, les données personnelles (IP, comportement) partent chez Google. Obligation légale d'afficher un bandeau de consentement, de gérer les refus, de tenir un registre de traitement. Pour un portfolio perso, c'est disproportionné.
  • Bloqué : uBlock Origin, Brave, Firefox Enhanced Tracking Protection — GA est bloqué chez une part non négligeable des devs qui constituent précisément mon audience. Les stats seraient sous-estimées.

L'alternative self-hosted type Umami ou Plausible requiert Node.js + PostgreSQL. Pas disponible ici.

La solution : log fichier, IP hashée, zéro cookie

Un fichier tracker.php inclus en haut de chaque page. Il écrit une ligne dans logs/visits.tsv à chaque visite. C'est tout.

La valeur ajoutée réelle est dans les choix de sécurité :

1. Aucun cookie posé — donc aucun consentement requis

Le RGPD impose le consentement uniquement si on stocke des données personnelles côté utilisateur (cookies) ou si on identifie une personne physique. Ici, rien de tout ça. Le serveur enregistre une ligne et c'est terminé — l'utilisateur ne sait pas, ne ressent rien, et c'est légal.

2. IP anonymisée avant stockage

L'IP est une donnée personnelle au sens du RGPD. On ne la stocke jamais en clair. À la place, on en calcule un hash SHA-256 salé, tronqué à 16 caractères :

$ip_hash = substr(
    hash('sha256', ($_SERVER['REMOTE_ADDR'] ?? '') . 'cv_salt_xK9!2026'),
    0, 16
);

Le sel rend le hash irréversible même avec une rainbow table. Le résultat permet de compter les visiteurs uniques sans jamais pouvoir retrouver l'IP d'origine. C'est exactement ce que recommande la CNIL pour l'exemption de consentement.

3. Filtrage des bots en amont

Sans filtrage, le fichier de log grossit rapidement avec du trafic inutile — crawlers Google, Bing, scrapers, outils de monitoring. Un regex sur le User-Agent élimine l'essentiel :

if (preg_match('/bot|crawl|spider|slurp|wget|curl|libwww|python|java|Go-http/i', $ua)) {
    return; // On sort immédiatement, rien n'est loggué
}

Ça ne filtre pas les bots qui se font passer pour des humains, mais ça couvre 95 % du trafic automatisé réel.

4. Dossier logs inaccessible depuis le web

Le fichier visits.tsv contient des hashes d'IP, des URLs, des dates. Même anonymisé, il ne doit pas être accessible publiquement. Un .htaccess dans le dossier logs/ suffit :

Deny from all

Apache refuse toute requête HTTP vers ce dossier. Le PHP serveur peut toujours y écrire en interne — seul l'accès navigateur est bloqué.

5. Variables isolées pour ne pas polluer l'espace global

Le tracker est inclus avec require dans index.php, donc il partage le même scope PHP. Pour éviter d'écraser des variables existantes ou d'exposer des données internes, toutes les variables du tracker sont préfixées $_ et supprimées avec unset() en fin de script. C'est rudimentaire mais efficace sans avoir à encapsuler dans une fonction.

Le dashboard

Un stats.php protégé par mot de passe lit le fichier TSV, calcule les KPIs et les affiche avec Chart.js (CDN). Pas de session persistante complexe — juste un $_SESSION PHP standard et une comparaison de mot de passe en clair (acceptable pour un accès purement local/admin sur un portfolio perso).

KPIs affichés :

  • Visites totales et visiteurs uniques sur 7 / 30 / 90 jours
  • Visites aujourd'hui vs hier
  • Graphique des visites par jour
  • Top pages consultées
  • Sources de trafic (direct / externe / navigation interne)
  • Répartition desktop / mobile / tablet
  • Heatmap des heures de consultation

Ce que ça ne fait pas

Pas de heatmaps, pas de funnels, pas de sessions reconstruites, pas de durée de visite. Pour un portfolio, ce niveau de détail est sans intérêt. Ce qui compte : est-ce que les gens arrivent, et quelles pages regardent-ils.

Le fichier de log va grossir. Il faudra le purger périodiquement ou mettre en place une rotation (un fichier par mois par exemple). Pour l'instant, un log de 100 000 lignes pèse environ 8 Mo — PHP le lit en moins d'une seconde.

Résultat

Zéro cookie. Zéro bandeau de consentement. Zéro dépendance externe en production. Zéro donnée personnelle identifiable stockée. Dashboard fonctionnel en moins de 200 lignes de PHP.

C'est le bon niveau de complexité pour le besoin réel.

Dashboard stats.php — KPIs, graphique visites par jour, top pages, sources de trafic et heatmap horaire
stats.php en production — KPIs, visites/jour, top pages, appareils, heatmap horaire. Cliquer pour agrandir.

Commentaires (0)