Skip to content

Add a dormant Matomo proxy mode, switched by MATOMO_MODE - #27

Open
matthewberry wants to merge 2 commits into
mainfrom
mjberry/matomo-runtime-proxy
Open

matthewberry wants to merge 2 commits into
mainfrom
mjberry/matomo-runtime-proxy

Conversation

@matthewberry

@matthewberry matthewberry commented Oct 29, 2026 •

Copy link
Copy Markdown
Contributor

Part of Phase 2 of the Matomo plan (upgrade, cookieless tracking, ad-block recovery). This change is dormant: with the default matomoMode: direct, tracking behaves as it does today.

What

  • MATOMO_MODE (chart config.matomoMode, optional in the Deployment, written to envvars.json). proxied turns the proxy on; anything else is direct.
  • App (src/main.ts). MatomoModule.forRoot({ mode: 'deferred' }), then an app initializer loads envvars.json and calls initializeTracker:
    • proxied: disableCookies(), tracker /px/h, script /px/s.js.
    • otherwise: https://matomo.mmli2.ncsa.illinois.edu/ as before.
  • nginx. default.conf includes /etc/nginx/matomo-proxy/*.conf. entrypoint.sh writes locations.conf there only when proxied:
    • /px/s.js → matomo.js
    • /px/h → matomo.php, appending X-Forwarded-For
    • both drop the app's Cookie and Authorization headers on the way to Matomo, and hide any Set-Cookie coming back
    • upstream MATOMO_UPSTREAM, default http://matomo.matomo.svc.cluster.local

The locations aren't in default.conf itself because nginx refuses to start when a proxy_pass host doesn't resolve (local docker, apps not on mmli2). The paths have no matomo/piwik and no .php, since EasyPrivacy blocks *.php?action_name=.

X-Forwarded-For is appended to, not overwritten: Traefik replaces any client-sent header with the real client IP, and Matomo (on release day, proxy_ips[] = 10.42.0.0/16 with proxy_ip_read_last_in_list = 1) reads from the right and skips the pod network. That needs proxy_client_headers[] = HTTP_X_FORWARDED_FOR too: it's commented out by default, and without it Matomo ignores the header and records the app pod's IP. All three lines are in the plan's release-day step. Using $remote_addr would record Traefik's IP.

Verification

  • docker build passes. ng test: 33/33 pass.
  • Direct mode starts, and headless Chrome sees hits to the Matomo host with _pk_id/_pk_ses cookies set.
  • Proxied mode with the in-cluster upstream fails to start off-cluster with host not found in upstream, as expected (this is why the flag gates the file).
  • Proxied mode with MATOMO_UPSTREAM=https://matomo.mmli2.ncsa.illinois.edu: nginx -t passes, /px/s.js serves the tracker, headless Chrome sends hits to /px/h with no cookies, and test hits to site 11 (SPICE test) were accepted with no Set-Cookie in the response.

Not verified: that Matomo records the real visitor IP through the proxy. That needs the cluster plus the release-day proxy_ips config. I also didn't check what headers reach Matomo, only the nginx config that strips them.

Not in this PR

Syncing to the apps, and the Helm values flip on release day.

With MATOMO_MODE=proxied, entrypoint.sh writes nginx locations that serve
matomo.js at /px/s.js and forward hits from /px/h to matomo.php, and the app
initializes the tracker against those paths with cookies disabled. Any other
value keeps today's direct tracking. The locations are written only when the
flag is on, because nginx won't start if a proxy_pass host doesn't resolve.

The tracker is now configured in deferred mode, after envvars.json loads.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant