Analytique sans surveillance

Ce que Google Analytics vous coûte vraiment

15.04.2026 | 27 Shawwal 1447
14 min read

بِسْمِ ٱللَّهِ ٱلرَّحْمَـٰنِ ٱلرَّحِيمِ

Vous avez un site web. Savez-vous ce qui s’y passe ?

La plupart ne le savent pas. Celui-ci ne le savait pas non plus. Le site était en ligne, des pages s’ajoutaient, des billets étaient écrits, le contenu traduit en cinq langues. Rien de tout cela ne produisait le moindre signal indiquant si quelqu’un lisait. Aucun nombre de pages vues, aucune donnée de référent, et aucun moyen de savoir si la page d’accueil faisait son travail ou si les visiteurs repartaient aussitôt.

La réponse évidente, c’est Google Analytics. On colle une balise de script, on obtient un tableau de bord. La plupart des sites font exactement ça. Mais Google Analytics n’est pas gratuit. Vous payez avec les données de vos visiteurs. Google associe chaque visite de page à tout ce qu’il sait déjà de cette personne : historique de recherche, habitudes YouTube, données de localisation. Votre site devient un point de plus dans un profil que votre visiteur n’a jamais consenti à construire.

S’y ajoute le problème du RGPD. Si votre activité touche des visiteurs de l’UE, Google Analytics exige une bannière de consentement juridiquement conforme. La plupart des implémentations s’y prennent mal. Le risque juridique est réel.

Le site avait besoin d’analytique. Pas de Google.

Définitions rapides
Que signifie auto-hébergé ?

Un logiciel qui tourne sur un serveur que vous contrôlez, et non sur la plateforme d’un tiers. Les données, la configuration et les accès restent chez vous.

Qu'est-ce qu'une analytique respectueuse de la vie privée ?

Une manière de compter et de comprendre les visiteurs de votre site sans les identifier individuellement. Pas de pistage entre sites, pas de profil de leur navigation ailleurs, aucun tiers qui touche aux données. Vous voyez ce qui se passe sur votre site ; personne d’autre.

Qu'est-ce qu'un réseau privé ?

Un réseau que seuls les membres peuvent voir. Les appareils en dehors du réseau ne savent même pas que les services qui s’y trouvent existent. Des outils d’administration comme un tableau de bord d’analytique peuvent y tourner sans jamais apparaître sur l’internet public.

À quoi ressemble l’alternative

L’outil s’appelle Umami.

Lien Externe umami.is

Open source, sous licence MIT, respectueux de la vie privée, sans cookies. Il tourne sur votre propre serveur. Inshallah les données ne quitteront jamais votre infrastructure. Pas besoin de bannière de consentement, parce qu’il n’y a rien à consentir. Pas de cookies, pas de données personnelles, pas de pistage entre sites.

Ce qu’il vous donne : pages vues, référents, ventilation par navigateur et par appareil, pays d’origine, et évènements personnalisés. Tableau de bord clair, données en temps réel. Un dirigeant d’entreprise peut inshallah l’ouvrir et comprendre ce qui se passe, sans formation.

Ce qu’il ne donne pas : profils individuels de visiteurs, enregistrements de session ni cartes thermiques. Il n’identifie pas les visiteurs un à un. Il vous montre ce qui s’est passé sur votre site.

Umami tournait déjà sur le serveur, derrière un VPN privé. C’est là que les décisions intéressantes commencent.

Qui peut voir le tableau de bord ?

La plupart des guides d’auto-hébergement d’analytique se terminent par «rendez-vous sur votre-domaine.fr et connectez-vous». Votre tableau de bord d’analytique se retrouve alors sur l’internet public. N’importe qui peut le trouver. Des scanners automatisés vont le découvrir. Des bots vont essayer les identifiants par défaut.

Le tableau de bord est un outil d’administration. Il a une page de connexion, une application web et une base de données derrière. Chacune de ces couches est une surface d’attaque. Si vous manquez une mise à jour de sécurité, le tableau de bord, et potentiellement le serveur, est exposé.

L’instance Umami de ce site n’existe pas sur l’internet public. Elle tourne derrière un réseau privé (NetBird).

Lien Externe netbird.io

Le tableau de bord n’est accessible que depuis les appareils qui font partie de ce réseau. Pour tous les autres, il n’y a pas de page de connexion, pas de message d’erreur, pas de réponse. Le domaine résout vers une IP qui n’existe qu’à l’intérieur du VPN ; depuis l’internet public, il n’y a rien à quoi se connecter. Il n’y a rien à attaquer parce qu’il n’y a rien à trouver.

C’est un principe qui dépasse de loin l’analytique : la forme la plus haute de la sécurité, c’est de ne pas être visible du tout.

Ce qui tourne en dessous

Le service d’analytique tourne dans deux conteneurs au sein d’un pod Podman : PostgreSQL et l’application Next.js, partageant un espace de noms réseau pour que l’application atteigne la base de données sans jamais l’exposer. Tout tourne sous un utilisateur dédié de type compte de service, sans shell, supervisé en même temps que les autres services du serveur. Beszel suit la santé des ressources (CPU, RAM, disque, réseau) ; Dozzle gère les journaux (ce que le service fait, les erreurs qu’il a rencontrées). Ressources et journaux répondent à des questions différentes, et un service auquel on fait confiance en production a besoin des deux.

Mais les visiteurs doivent y accéder

Si le service d’analytique est caché de l’internet public, comment les navigateurs des visiteurs lui envoient-ils des données ?

Ils ne parlent pas directement au service d’analytique. Ils parlent au site lui-même. Le serveur web (Caddy) joue le rôle de proxy et transfère deux chemins précis vers le service d’analytique en interne. Avec les valeurs par défaut documentées d’Umami, cela ressemble à peu près à ceci :

(umami_proxy) {
handle /umami.js {
reverse_proxy 127.0.0.1:8089
}
handle /api/send {
reverse_proxy 127.0.0.1:8089
}
}

Un chemin sert le script de pistage. L’autre reçoit les données d’analytique. Les deux sont importés dans le bloc Caddy du site :

example.com {
import tls_config
root * /var/www/example.com
import umami_proxy
import static_files
}
Not reachable from public internetHTTPStwo proxied pathsNetBird tunnelVisitor browserAdmin device on NetBirdCaddy reverse proxyUmami appPostgreSQL

Du point de vue du visiteur, les deux requêtes vont vers le même domaine que le site, sans service externe et sans requête cross-origin. Le navigateur charge un script depuis le site et renvoie des données vers le site.

Le service d’analytique traite tout en arrière-plan, totalement invisible pour le visiteur et totalement inaccessible de l’extérieur.

Pour une entreprise, aucun fournisseur ne se trouve entre vous et votre analytique. Inshallah les tarifs ne pourront pas changer du jour au lendemain, les fonctionnalités ne peuvent pas être supprimées, et le produit ne peut pas être racheté puis fermé. Les données historiques restent sur le serveur que vous contrôlez, interrogeables et exportables selon vos conditions.

Le problème invisible : les bloqueurs de publicité

Les bloqueurs de publicité bloquent aussi l’analytique respectueuse de la vie privée.

Umami n’utilise pas de cookies. Il ne piste pas les individus. Il ne partage aucune donnée avec quiconque. Mais les principales listes de blocage (EasyPrivacy, uBlock Origin, AdGuard) contiennent des règles qui visent Umami spécifiquement. Les bloqueurs n’évaluent pas l’éthique. Ils font correspondre des motifs :

Les estimations de l’usage des bloqueurs de publicité se situent généralement entre 10 et 40 pour cent, plus haut pour les publics jeunes et techniquement avertis. Ces visiteurs n’apparaîtront jamais dans votre analytique. Vous prendrez des décisions sur la base de données incomplètes sans savoir qu’elles sont incomplètes.

L’auto-hébergement règle le problème de la souveraineté des données. Il ne règle pas le problème des bloqueurs. La solution doit aller plus loin.

Faire disparaître l’analytique

La solution au problème des bloqueurs est conceptuelle, pas technique. Faites en sorte que les requêtes d’analytique ressemblent à des assets ordinaires du site. Un autre nom pour le script, un autre chemin pour l’endpoint, l’un et l’autre se fondant dans ce que le site charge déjà. Les motifs des listes de blocage ne correspondent plus.

Un reverse proxy fait correspondre un chemin public à ce que l’application attend en interne. Le navigateur voit une requête same-origin ordinaire ; le service d’analytique reçoit les données derrière.

Quand la configuration vous trompe

C’est ce qui a pris le plus de temps. C’est là que la différence entre «j’ai suivi un guide» et «je comprends le système» se voit.

Umami expose deux variables d’environnement pour le renommage. TRACKER_SCRIPT_NAME contrôle le nom de fichier sous lequel le serveur sert le script de pistage. COLLECT_API_ENDPOINT contrôle le chemin auquel le tracker envoie les données. Les deux ressemblent à de la configuration d’exécution ordinaire.

TRACKER_SCRIPT_NAME en est. Le nom du script est décidé au moment où le serveur répond à une requête. On définit la variable, on redémarre, le serveur sert le script sous le nouveau nom. Voilà.

COLLECT_API_ENDPOINT est le piège. Après l’avoir définie et redémarré, le routage côté serveur obéissait. Il acceptait les requêtes au nouveau chemin. Tout semblait correct. Mais dans le navigateur, le tracker envoyait toujours vers /api/send. Le proxy ne connaissait pas ce chemin, donc les données partaient dans le vide. Le tableau de bord affichait zéro visiteur. Aucune erreur nulle part. Tout avait l’air bien. Rien ne fonctionnait.

Le code source l’explique. Le script de pistage est construit par Rollup pendant la construction de l’image Docker :

rollup.tracker.config.js
plugins: [
replace({
__COLLECT_API_ENDPOINT__: process.env.COLLECT_API_ENDPOINT || '/api/send',
}),
]
Lien Externe github.com/umami-software/umami/blob/master/rollup.tracker.config.js

L’endpoint est compilé sous forme de chaîne littérale dans la sortie JavaScript. À l’exécution, la variable d’environnement modifie l’endroit où le serveur écoute, mais le navigateur exécute du code précompilé avec l’ancien chemin gravé dedans.

L’image Docker officielle de ghcr.io/umami-software/umami est livrée avec /api/send codé en dur dans le script de pistage. Définir COLLECT_API_ENDPOINT à une nouvelle valeur dans le fichier .env fait que le serveur accepte les requêtes au nouveau chemin, mais le tracker continue de demander au navigateur d’envoyer vers /api/send. Les deux côtés se contredisent en silence.

GET /umami.jsforwardtracker script (with /api/send baked in)POST /api/sendPre-compiled path ignores the runtime configNo handler matches this pathDashboard: zero visitors, no errorsBrowserCaddyUmami

La solution est de construire l’image depuis les sources avec la nouvelle valeur incorporée à la compilation. Le Dockerfile d’Umami n’expose pas cette variable comme argument de build de base, mais deux lignes dans l’étape builder comblent le manque :

ARG COLLECT_API_ENDPOINT=/api/send
ENV COLLECT_API_ENDPOINT=$COLLECT_API_ENDPOINT

ARG déclare une variable disponible au moment du build ; ENV la rend visible à Rollup pour que la valeur soit compilée dans le script de pistage. Avec ces deux lignes en place, construire avec une valeur personnalisée tient en un seul drapeau :

Terminal window
podman build --build-arg COLLECT_API_ENDPOINT=<your-endpoint> -t umami-custom:latest .

Le résultat est la même image que l’officielle, avec une chaîne différente dans un seul fichier JavaScript.

Si l’hôte fait tourner Podman en rootless avec un utilisateur de compte de service séparé, l’image construite sous votre utilisateur développeur n’est pas visible par cet utilisateur de service. Chaque utilisateur rootless a son propre stockage d’images isolé. Le transfert tient en deux commandes :

Terminal window
podman save localhost/umami-custom:latest -o /tmp/umami-custom.tar
cd /tmp && sudo -u umami podman load -i /tmp/umami-custom.tar

Le cd /tmp compte. sudo -u umami hérite par défaut du répertoire de travail de l’appelant. Si votre shell se trouve dans votre home de développeur, le chargement échoue avec cannot chdir: Permission denied parce que l’utilisateur de service ne peut pas lire dans votre home. L’erreur pointe sur chdir, pas sur sudo ni podman, donc la cause n’est pas évidente la première fois qu’on tombe dessus.

Cela couvre à quoi ressemble un build Umami personnalisé en général. Ce que ce site met dans <your-endpoint>, et le chemin de script correspondant que le proxy public réécrit, ne figure pas dans ce billet.

Côté Astro

Le script de pistage doit se charger sur chaque page. Dans Astro, cela veut dire l’ajouter au layout partagé :

<script is:inline defer
src="/umami.js"
data-website-id="..."
data-host-url="/">
</script>

Quelques points à noter.

is:inline dit à Astro de laisser la balise de script telle qu’elle est écrite et de ne pas la traiter ni la bundler. Le tracker est un script tiers de type fire-and-forget ; il n’a pas besoin de la pipeline de bundling d’Astro.

defer charge le script sans bloquer le rendu de la page. L’analytique ne doit jamais ralentir la page.

La référence d’Astro ajoute une note qui se lit plus largement que sa portée réelle :

Will not be bundled into an external file. This means that attributes like defer which control the loading of an external file will have no effect.

Lien Externe docs.astro.build/en/reference/directives-reference

Cela décrit le cas du contenu inline, où il n’y a aucun fichier externe dont reporter le chargement. Pour un script avec src, is:inline dit seulement à Astro de ne pas traiter la balise. La sortie dist l’émet telle quelle, defer intact, et le navigateur applique l’attribut normalement.

data-host-url="/" redéfinit l’URL de base que le tracker utilise pour construire son endpoint. Sans cela, le tracker dérive la base depuis l’emplacement de son propre script. Un script à un chemin de sous-répertoire (par exemple /path/to/umami.js) enverrait vers /path/to/api/... au lieu de /api/.... Mettre la valeur à / (ou à la racine du site, peu importe ce que c’est) garde le chemin correct.

Ce site a aussi une 404.html autonome qui se trouve hors de la pipeline Astro (un contournement d’un bug de routage du framework, documenté dans un billet à part).

Lien Interne La page 404 qui n'arrêtait pas de disparaître

La même balise de script y est ajoutée à la main, puisqu’elle ne passe pas par le layout partagé.

Pourquoi la recette reste privée

La solution conceptuelle est déjà nommée : faire que le script et l’endpoint ressemblent à des assets ordinaires du site. Ce site fait cela. Les noms précis, et le raisonnement qui rattache le choix des noms au profil de contenu d’un site, ne figurent pas dans ce billet.

Le coût pour faire tourner cela est réel. L’image en amont peut être mise à jour avec podman pull. Un build personnalisé veut dire reconstruire depuis les sources à chaque sortie en amont.

Les bloqueurs de publicité existent parce que les trackers ont mérité la défiance. La plupart de l’analytique sur l’internet public est vraiment de la surveillance : scripts tiers, fingerprinting, données transmises en aval. Les bloqueurs ne peuvent pas distinguer les sites honnêtes. Ils bloquent les motifs.

Une contre-mesure par correspondance de motifs aide les sites honnêtes et les malhonnêtes pareillement. Un billet public avec une recette qui marche est la même clé tendue à un dentiste qui compte ses rendez-vous et à une firme de pistage qui essaie de remettre son pixel sur l’écran de quelqu’un. Le premier usage est légitime. Le second est ce qui a rendu les bloqueurs nécessaires.

Le billet s’arrête donc au principe.

La configuration complète, les noms précis et le raisonnement qui les choisit pour un site donné, reste hors de la documentation publique pour la raison ci-dessus. Si cela concerne votre situation, la page À propos est le bon point de départ.

Et la vitesse de chargement ?

Un site rapide retient les visiteurs. Les pages lentes les perdent, dégringolent dans le classement de recherche et ont l’air peu professionnelles. L’analytique doit se peser face à ce coût.

Il existe une solution connue pour les scripts d’analytique lourds : Partytown. Il déplace le script dans un thread d’arrière-plan pour que la page principale reste réactive pendant que le tracker fait son travail. Pour des sites qui chargent quelque chose comme Google Tag Manager ou Facebook Pixel (50 à 200 Ko de JavaScript qui sondent et injectent en continu), Partytown est un vrai gain de vitesse.

Cette configuration n’en a pas besoin.

Le tracker Umami pèse 2,63 Ko non compressé, 1,43 Ko après gzip. Il se charge avec defer, déclenche une seule requête POST quand une page est visitée, puis se termine.

Ajouter Partytown rendrait en fait les choses plus lentes. Son runtime tourne autour de 15 Ko gzippés, le tout chargé dans le navigateur du visiteur : un loader de 1,2 Ko plus un worker de 13,6 Ko qui intercepte les requêtes en arrière-plan. Rien de cela ne tourne sur le serveur. Emballer un script de 1,43 Ko dans 15 Ko de surcoût, c’est dix fois plus de JavaScript que le script lui-même.

Taille de script gzippée : tracker Umami face au runtime de Partytown (loader plus worker)

La réponse honnête est la plus simple. Un tracker assez petit pour que la page reste rapide, parce que rien ne la ralentit.

En fin de compte

Chaque pièce de ce setup règle un problème précis. Umami remplace Google Analytics sans la surveillance. Le réseau privé garde le tableau de bord hors de l’internet public. Le proxy Caddy permet aux visiteurs d’atteindre l’analytique à travers le site lui-même. Le problème des bloqueurs a une solution que ce billet pointe sans la dérouler. Chaque décision prise isolément est petite.

Ensemble, elles décident de deux choses : si vous savez ce qui se passe sur votre site, et si la relation avec vos visiteurs reste la vôtre.

Inshallah cela vaut la peine d’être bien fait.

FAQ
Faut-il le contournement des bloqueurs de publicité, ou l'auto-hébergement suffit-il ?

L'auto-hébergement seul règle la souveraineté des données. Le problème des bloqueurs de publicité est indépendant : avec les endpoints par défaut, une part connue de vos visiteurs n'apparaîtra toujours pas dans vos chiffres.

Si votre seul objectif est d'arrêter de transmettre des données à Google, une installation par défaut d'Umami suffit. Si vous voulez que le compteur de visiteurs reflète la réalité, l'approche par renommage via le proxy comble cet écart.

Combien cela coûte-t-il par rapport à Google Analytics ?

Le coût d'un serveur, pas un abonnement. L'empreinte est une base de données et une petite application Node.js qui tourne aux côtés de tout ce que vous hébergez déjà. Pas de tarification par visite, par évènement ni par fonctionnalité. Le modèle est : vous payez pour le serveur dont vous avez déjà besoin.

Umami est-il conforme au RGPD dès l'installation ?

Plus proche que Google Analytics, mais pas automatique. Umami n'utilise pas de cookies et ne partage pas de données avec des tiers, ce qui élimine les manquements de conformité les plus courants.

Certains points demandent encore votre attention : votre politique de confidentialité, le pays où tourne votre serveur, et si vous stockez les adresses IP.

Puis-je transférer mon historique Google Analytics ?

En général non. Umami commence à collecter dès le moment de son installation ; les données historiques de GA restent à l'intérieur du produit Google. Certains exportent les rapports GA pour archivage, mais les métriques ne s'alignent pas assez proprement pour être fusionnées avec Umami à partir de là.

Ingénieur aérospatial

Entrepreneur éthique en public

Vous gérez votre activité

Je m'occupe du côté numérique

Travailler avec moi
  • IA avec honnêteté
  • Infrastructure privée
  • Sites web performants

Parlez-moi de votre situation :

javed@javedab.com En savoir plus