feat: self-hosted cookieless Matomo, remove foreign tracking #18

Merged
Phil merged 2 commits from feat/matomo-analytics into main 2026-07-09 17:49:00 +00:00
Owner

What & why

The site was shipping a hardcoded Matomo Tag Manager snippet pointing at a third-party instance (krisnetics.biz) — a leftover from a previous dev, sending visitor data to a foreign endpoint without consent (DSGVO problem). This replaces it with Phil's own self-hosted, cookieless Matomo.

Closes #17

Changes

Frontend / config (c323544)

  • Remove the foreign krisnetics.biz MTM snippet from _components/core/footer.twig.
  • New _system/analytics.twig — cookieless _paq tracker (disableCookies before trackPageView), rendered only when MATOMO_URL + MATOMO_SITE_ID are set. Included before </body> in _layouts/base.twig.
  • New config/custom.php surfaces the two values to Twig via craft.app.config.custom.*.
  • Empty locally ⇒ no tracking in dev.

Infra (b07b313)

  • docker-compose.traefik.yml: new matomo (matomo:5.11-apache, current stable 5.11.2) + isolated matomo_db (mariadb:11.4) services, Traefik-routed at ${MATOMO_DOMAIN} with LE. No host-port mapping. DB pre-wired so the installer skips the DB step.
  • Forward MATOMO_URL / MATOMO_SITE_ID into the php service; document all vars in .env.production.example.

Post-merge deploy steps (not in code)

  1. DNS analytics.philflow.io → prod host — done by Phil.
  2. Add MATOMO_DOMAIN, MATOMO_DB_PASSWORD, MATOMO_DB_ROOT_PASSWORD (generated) to prod /opt/philflow/.env.
  3. Deploy per docs/DEPLOYMENT.md (DB backup → rsync → docker compose -f docker-compose.traefik.yml up -d).
  4. One-time Matomo web installer: create super-user, add site philflow.io → note site ID.
  5. Matomo admin: IP anonymization (2 bytes) + global disable-cookies. Put site ID + MATOMO_URL in prod .env, restart php.

Verification

  • Local: page renders no Matomo snippet (env empty).
  • Prod: analytics.philflow.io HTTPS reachable; visit records a hit; no _pk_* cookies (cookieless); no krisnetics.biz in HTML.

Caveat (honest)

Cookieless-JS in DE (TDDDG) reduces legal risk but is not a guaranteed consent exemption — accepted trade-off (no banner). Matomo's fully consent-exempt path would be server-side tracking.

🤖 Generated with Claude Code

## What & why The site was shipping a hardcoded Matomo Tag Manager snippet pointing at a **third-party instance** (`krisnetics.biz`) — a leftover from a previous dev, sending visitor data to a foreign endpoint without consent (DSGVO problem). This replaces it with Phil's own self-hosted, cookieless Matomo. Closes #17 ## Changes **Frontend / config** (`c323544`) - Remove the foreign `krisnetics.biz` MTM snippet from `_components/core/footer.twig`. - New `_system/analytics.twig` — cookieless `_paq` tracker (`disableCookies` before `trackPageView`), rendered only when `MATOMO_URL` + `MATOMO_SITE_ID` are set. Included before `</body>` in `_layouts/base.twig`. - New `config/custom.php` surfaces the two values to Twig via `craft.app.config.custom.*`. - Empty locally ⇒ **no tracking in dev**. **Infra** (`b07b313`) - `docker-compose.traefik.yml`: new `matomo` (`matomo:5.11-apache`, current stable 5.11.2) + isolated `matomo_db` (`mariadb:11.4`) services, Traefik-routed at `${MATOMO_DOMAIN}` with LE. No host-port mapping. DB pre-wired so the installer skips the DB step. - Forward `MATOMO_URL` / `MATOMO_SITE_ID` into the `php` service; document all vars in `.env.production.example`. ## Post-merge deploy steps (not in code) 1. DNS `analytics.philflow.io` → prod host — **done by Phil**. 2. Add `MATOMO_DOMAIN`, `MATOMO_DB_PASSWORD`, `MATOMO_DB_ROOT_PASSWORD` (generated) to prod `/opt/philflow/.env`. 3. Deploy per `docs/DEPLOYMENT.md` (DB backup → rsync → `docker compose -f docker-compose.traefik.yml up -d`). 4. One-time Matomo web installer: create super-user, add site `philflow.io` → note **site ID**. 5. Matomo admin: IP anonymization (2 bytes) + global disable-cookies. Put site ID + `MATOMO_URL` in prod `.env`, restart `php`. ## Verification - Local: page renders **no** Matomo snippet (env empty). - Prod: `analytics.philflow.io` HTTPS reachable; visit records a hit; **no** `_pk_*` cookies (cookieless); no `krisnetics.biz` in HTML. ## Caveat (honest) Cookieless-JS in DE (TDDDG) reduces legal risk but is **not** a guaranteed consent exemption — accepted trade-off (no banner). Matomo's fully consent-exempt path would be server-side tracking. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
The site shipped a hardcoded Matomo Tag Manager snippet pointing at a
third-party instance (krisnetics.biz) left behind by a previous dev,
sending visitor data to a foreign endpoint without consent. Replace it
with our own config-driven, cookieless _paq tracker rendered only when
MATOMO_URL + MATOMO_SITE_ID are set (empty locally = no tracking).

Cookieless via disableCookies() before trackPageView; a consent banner
is intentionally omitted (accepted DE/TDDDG risk trade-off).

Refs #17

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add matomo (matomo:5.11-apache, current stable 5.11.2) and an isolated
matomo_db (mariadb:11.4) to docker-compose.traefik.yml, routed by Traefik
at ${MATOMO_DOMAIN} with Let's Encrypt. No host-port mapping (avoids the
redis_livekit-style conflicts on the shared host). DB is pre-wired via
env so the web installer skips the database step. Forward MATOMO_URL /
MATOMO_SITE_ID into the php service and document all vars in the prod
env example.

Refs #17

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phil merged commit df6ac86475 into main 2026-07-09 17:49:00 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
Phil/philflow.io!18
No description provided.