Plausible Analytics: privacy-vriendelijke website statistieken met self-hosting
Elke website-eigenaar wil weten hoeveel bezoekers er zijn, waar ze vandaan komen en welke pagina’s ze bekijken. Google Analytics is de standaard, maar komt met een prijs: je verkoopt de privacy van je bezoekers aan Google’s advertentie-imperium. Plausible Analytics is het privacy-vriendelijke alternatief. In dit artikel beschrijf ik wat Plausible is, waarom ik het op mijn eigen server draai, en de technische hindernissen die ik moest overwinnen.
Wat is Plausible?
Plausible Analytics is een open source web analytics tool — een alternatief voor Google Analytics maar dan zonder cookies, zonder tracking van persoonlijke data, en zonder toestemmingsbanners. Het is volledig AVG/GDPR-compliant out-of-the-box.
Plausible toont je in één overzichtelijk dashboard:
- Unieke bezoekers en paginaweergaven
- Verkeersbronnen (direct, social, zoekmachines)
- Best bezochte pagina’s
- Bezoekerslocaties (op landniveau, geen exacte locatie)
- Apparaat- en browserstatistieken
De tracking script is slechts 3 KB — een fractie van Google Analytics’ 45+ KB. Dit heeft nauwelijks impact op de laadtijd van je site.
Plausible Community Edition
Plausible biedt twee smaken:
| Plausible Cloud | Plausible Community Edition | |
|---|---|---|
| Hosting | Managed, EU-servers in Duitsland | Self-hosted op eigen infra |
| Updates | Continu, meerdere keren per week | 2x per jaar (long-term release) |
| Premium features | Funnels, ecommerce, user journeys | Basis analytics |
| Support | Premium support | Community support |
| Kosten | Vanaf €9/maand | Gratis (alleen serverkosten) |
| Licentie | Proprietary | AGPLv3 |
Plausible CE is “free as in beer” — je draait exact dezelfde code op je eigen server, zonder ooit te betalen aan Plausible. Het project wordt gefinancierd door de cloud-abonnees, niet door investeerders of advertentiedata.
Waarom self-hosting?
Voor eddydevink.nl koos ik voor de Community Edition op mijn eigen server. Mijn overwegingen:
- Privacy-first: Alle bezoekersdata blijft op mijn eigen hardware. Geen derde partij die meekijkt.
- Geen maandelijkse kosten: Mijn NUC server draait toch al 24/7 voor Nextcloud, Forgejo en CouchDB. Plausible erbij kost niets extra.
- Controle: Ik bepaal zelf hoe lang data wordt bewaard, of er überhaupt data wordt gelogd, en wie erbij kan.
- Leerzaam: Het opzetten van een Docker Compose stack met PostgreSQL, ClickHouse, Elixir/Phoenix en nginx is een mooie technische uitdaging.
- Geen cookiemuur: Plausible gebruikt geen cookies, dus je hoeft geen irritante cookiebanner te tonen. Dit is zowel netter voor bezoekers als AVG-technisch juist.
De architectuur
Plausible CE draait als een Docker Compose stack met drie containers:
┌─────────────────────────────────────────────┐
│ nginx │
│ (reverse proxy + HTTPS) │
│ plausible.eddydevink.nl → 127.0.0.1:8000 │
└─────────────────┬───────────────────────────┘
│
┌─────────────┴─────────────┐
│ Plausible (Elixir) │
│ v3.2.1 CE │
│ poort 8000 (intern) │
└─────┬──────────────┬───────┘
│ │
┌─────┴──────┐ ┌────┴──────────┐
│ PostgreSQL │ │ ClickHouse │
│ (config) │ │ (analytics) │
│ poort 5432 │ │ poort 8123 │
└────────────┘ └───────────────┘
- PostgreSQL — gebruikersaccounts, site-configuratie, instellingen
- ClickHouse — column-store database voor de analytics-data. Extreem snel voor time-series queries
- Plausible — de Elixir/Phoenix webapplicatie die het dashboard serveert en de tracking API afhandelt
De tracking integratie met Hugo is verrassend simpel. Een partial template in het theme voegt het script alleen toe in productie-builds:
{{ if hugo.IsProduction }}
<script defer data-domain="{{ .Site.BaseURL | replaceRE "^https?://(www\\.)?" "" | replaceRE "/$" "" }}"
src="https://{{ .Site.Params.plausibleServerDomain }}/js/script.js"></script>
{{ end }}
Tijdens hugo server (lokaal ontwikkelen) wordt het script niet geladen. Alleen na hugo --minify en deployen naar productie verschijnt het.
Beveiliging
Self-hosting betekent dat je zelf verantwoordelijk bent voor de beveiliging. Dit zijn de maatregelen die ik heb genomen:
1. Alleen bereikbaar via nginx
Plausible draait op 127.0.0.1:8000 — uitsluitend bereikbaar vanaf localhost. nginx fungeert als enige ingang op poort 443 met Let’s Encrypt, security headers, en user-agent blocking.
2. Registratie uit
Na het aanmaken van het admin-account heb ik DISABLE_REGISTRATION=true gezet. Niemand kan een account registeren — zelfs niet als ze de URL weten.
3. Fail2Ban op login
Een custom jail beschermt tegen brute-force aanvallen op het login-formulier:
[plausible]
enabled = true
filter = plausible-login
maxretry = 3
bantime = 86400 # 24 uur
findtime = 43200 # 12 uur
Bij 3 foutieve inlogpogingen binnen 12 uur wordt het IP geblokkeerd in firewalld en gerapporteerd aan AbuseIPDB. Eigen IP-adressen (LAN, Tailscale, publiek) staan op de whitelist.
4. Database credentials
Het standaard PostgreSQL-wachtwoord (postgres) is vervangen. ClickHouse draait zonder authenticatie, maar is alleen bereikbaar binnen het Docker-netwerk.
5. Security headers in nginx
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: camera=(), microphone=(), geolocation=()
De installatie
Stap 1: DNS en nginx
Een nieuw A-record voor plausible.eddydevink.nl wijst naar het publieke IP van de Nextcloud server. nginx vangt het verkeer op poort 443 af en proxyt naar 127.0.0.1:8000:
server {
server_name plausible.eddydevink.nl;
location / {
proxy_pass http://127.0.0.1:8000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /live/websocket {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
De /live/websocket endpoint is nodig voor realtime updates in het dashboard — zonder deze locatie kon het dashboard niet live tonen of er nieuwe bezoekers binnenkomen.
Stap 2: Docker Compose
Plausible CE draait via een standaard Docker Compose setup. De belangrijkste aanpassing was het compose.override.yml bestand:
services:
plausible:
ports:
- 127.0.0.1:8000:8000
environment:
- CLICKHOUSE_DATABASE_URL=http://plausible_events_db:8123/plausible_events_db
Stap 3: Hugo integratie
De tracking partial in het Hugo theme zorgt voor de juiste data-domain (zonder www. — zie probleem 1 hieronder) en laadt het script alleen in productie.
De problemen — en hoe ik ze oploste
De installatie verliep niet vlekkeloos. Drie problemen hielden me een uur bezig.
Probleem 1: www. vs naakt domein
Symptoom: Na de installatie bleef het dashboard “Awaiting your first pageview” tonen, ondanks dat de site bezocht werd met het tracking script.
Analyse: Plausible slaat domeinen op zonder www.-prefix. Wanneer je in de setup wizard www.eddydevink.nl invoert, wordt dit automatisch opgeslagen als eddydevink.nl. Maar de data-domain in het tracking script bevatte nog steeds www.eddydevink.nl. De domain lookup faalde — het event werd gedropt met de status dropped_not_found.
De ingest_counters tabel in ClickHouse onthulde dit:
eddydevink.nl dropped_not_found (events met 'eddydevink.nl')
plausible.eddydevink.nl dropped_not_found (events met verkeerde data-domain)
Geen enkel event met www.eddydevink.nl werd geteld — niet als success, niet als drop.
Oplossing: De Hugo partial stript nu de www. prefix uit baseURL met replaceRE "^https?://(www\\.)?" "". De data-domain wordt eddydevink.nl, wat exact matcht met Plausible’s database.
Probleem 2: CLICKHOUSE_DATABASE_URL ontbrak
Symptoom: API calls naar /api/event retourneerden netjes HTTP 202 Accepted, maar geen enkel event verscheen in de ClickHouse events_v2 tabel. De Elixir.Plausible.Event.WriteBuffer in het Elixir-proces was leeg — nergens in het logs verscheen een event-processing melding.
Analyse: De compose.yml van Plausible CE declareert CLICKHOUSE_DATABASE_URL als een optionele omgevingsvariabele die uit de host wordt doorgegeven. Docker Compose leest .env echter alleen voor variabele interpolatie in de YAML — niet voor het doorgeven aan containers. De environment variable CLICKHOUSE_DATABASE_URL moest expliciet in compose.override.yml onder environment: gezet worden.
Zonder deze variabele wist Plausible niet waar ClickHouse te vinden was. Events werden geaccepteerd door de HTTP handler maar de ingest pipeline kon ze nergens kwijt — een stille fout.
Oplossing: CLICKHOUSE_DATABASE_URL=http://plausible_events_db:8123/plausible_events_db toegevoegd aan compose.override.yml. Na een docker compose up -d stroomden de events direct door naar ClickHouse.
Probleem 3: Volume reset bij debuggen
Symptoom: Tijdens het debuggen draaide ik docker compose down -v (volumes verwijderen) — een standaard reflex bij Docker-problemen. Alle data was weg: gebruiker, site-configuratie, 2FA sleutels, analytics historie.
Oplossing: DISABLE_REGISTRATION tijdelijk uitgecommentarieerd, account opnieuw geregistreerd via de web-UI, site opnieuw toegevoegd, en DISABLE_REGISTRATION weer aangezet. De 2FA moest opnieuw ingesteld worden. Les geleerd: -v is destructief — next time eerst docker compose down (zonder -v) proberen.
Conclusie
Plausible Analytics is nu live op https://plausible.eddydevink.nl. De Hugo site op www.eddydevink.nl stuurt bezoekersgegevens naar de self-hosted instantie. Het dashboard toont real-time stats: unieke bezoekers, paginaweergaven, verkeersbronnen, landen, browsers — allemaal zonder cookies, zonder privacy-schendingen, en zonder maandelijkse kosten.
De drie technische hindernissen — domein-stripping, ontbrekende ClickHouse URL, en volume-reset — lijken achteraf eenvoudig, maar het ontbreken van duidelijke foutmeldingen maakte de diagnose lastig. Events verdwenen spoorloos; nergens in de logs was een error te vinden. De oplossing lag in het methodisch doorlopen van de pipeline: nginx access log → API response → in-memory WriteBuffer → ClickHouse ingest_counters → events_v2 tabel.
Wat ik heb geleerd:
- Plausible stript
www.van domeinen — altjijd controleren ofdata-domainmatcht met de database - Docker Compose
.envbestanden zijn voor YAML-interpolatie, niet voor container environment docker compose down -vis een kernwapen — gebruik het met beleid- Fail2Ban voor Plausible vereist een custom filter (
plausible-login.conf) omdat Plausible foute logins retourneert met HTTP 200 (form re-render), niet met 401/422
Benieuwd naar Plausible? De Community Edition is gratis en relatief eenvoudig te installeren op elke server met Docker. De source code staat op GitHub.