Guide: How to Run Matomo Tag Manager Fully First-Party (Avoiding AdBlockers)

Modern adblockers increasingly block tracking based on hostname patterns (e.g. analytics.*, stats.*) and file paths such as matomo.php.
Even if you customise the request path, some filters still match the hostname.

To avoid this, you can make Matomo Tag Manager load entirely through the first-party domain of each tracked site.
The browser will only communicate with the site’s own domain. All communication with the actual Matomo server takes place server-side via reverse proxy.

This preserves legitimate analytics, respects consent management, and avoids accidental blocking by over-aggressive filter lists.

Tested and works with: Adguard on, Brave browser :slight_smile:


:bullseye: Goal

Convert third-party tracking:

https://matomo-server.tld/js/container_ABC.js
https://matomo-server.tld/matomo.php

Into completely first-party URLs like:
example.com = domain to track

https://example.com/app-container.js
https://example.com/app-collect

(You can name them anything — avoid words like “analytics”, “matomo”, “stats”, “track”, “collect.php”, “pixel.php” etc.)

Behind the scenes:

example.com → Nginx reverse proxy → Matomo server


:rocket: 1. Add Reverse Proxy Rules (on EACH tracked site)

In your site’s Nginx config
(or Plesk → Domain → Apache & Nginx settings → Additional Nginx directives):

Replace:

  • MATOMO_SERVER with your actual Matomo host

  • CONTAINER_FILE.js with your container script

  • /app-container.js and /app-collect with any names you prefer

# First-party Matomo tracking endpoint
location = /app-collect {
    proxy_pass https://MATOMO_SERVER/path/to/matomo.php;
    proxy_set_header Host MATOMO_SERVER;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_redirect off;
}

# First-party Tag Manager container script
location = /app-container.js {
    proxy_pass https://MATOMO_SERVER/path/to/js/CONTAINER_FILE.js;
    proxy_set_header Host MATOMO_SERVER;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header X-Forwarded-Host $host;
    proxy_redirect off;
}

Tip:
Use neutral path names such as:

/app.js
/app-config.js
/core.js
/app-endpoint
/api-event
/api-hit

Avoid sensitive words like:

analytics
matomo
stats
pixels
tracking
collect.php
events.php
monitor


:gear: 2. Configure Matomo Tag Manager

Inside Matomo:

Tag Manager → Container → Variables → Matomo Configuration

Set these:

Matomo URL

Your first-party domain (the site being tracked):

https://example.com

Custom Tracking Request Target Path

The path you created in Nginx:

app-collect

(No leading slash.)

Matomo will now send hits to:

https://example.com/app-collect?... 

Nginx will forward these internally to your actual Matomo server.

Publish the container afterwards.


:laptop: 3. Update Your Tag Manager Snippet

Replace the default script source:

<script src="https://MATOMO_SERVER/path/to/js/CONTAINER_FILE.js"></script>

with your first-party proxied version:

<script src="https://example.com/app-container.js"></script>

This ensures the browser never loads anything from your Matomo server directly.


:tada: Done!

You now have:

:check_mark: Fully first-party Matomo Tag Manager
:check_mark: Container script served from the site’s own domain
:check_mark: Tracking endpoint served from the site’s own domain
:check_mark: Adblockers see NO references to matomo.php, analytics hosts, or tracking keywords
:check_mark: Requests are internal-only between your site and Matomo
:check_mark: Works with aggressive blocker lists
:check_mark: No modification to Matomo core
:check_mark: Safe for updates and easy to replicate across multiple sites


:memo: Notes

  • Repeat the proxy block for each site you want to track.

  • Each site still uses the same central Matomo installation.

  • Choose endpoint names that do not contain typical tracking words.

  • Keep caching of the container JS modest (1 hour recommended) to keep tags fresh.

  • This does not bypass user consent - you should still honour Do-Not-Track and your CMP

:warning: Oopsie! Visitor IPs & Geo-location Stop Working Without This Fix

One thing I noticed after implementing this first-party proxy setup is that all visitor IPs and locations suddenly appeared as the proxy server’s IP inside Matomo.
That’s expected behaviour — because Matomo now only sees your server forwarding the request, not the real client.

Here’s the fix:

  1. Tell Matomo to trust and use those headers instead of REMOTE_ADDR.

Open config/config.ini.php on your Matomo server and add the following under [General]:

[General]
; ... your other settings ...

; Use proxy headers for client IP
proxy_client_headers[] = HTTP_X_FORWARDED_FOR
proxy_client_headers[] = HTTP_X_REAL_IP
proxy_client_headers[] = HTTP_CLIENT_IP

; Use forwarded host header if present
proxy_host_headers[] = HTTP_X_FORWARDED_HOST

; OPTIONAL: tell Matomo which IPs are trusted proxies such as your nginx server
; Only requests coming from these IPs will have their
; X-Forwarded-For headers interpreted.
;proxy_ips[] = 1.2.3.4
;proxy_ips[] = 5.6.7.8

After adding this:

  • Proxied sites using the first-party endpoint will show correct visitor IPs and GeoIP again.

  • Non-proxied sites will continue working normally — if the headers aren’t present, Matomo falls back to the usual REMOTE_ADDR.

This restores proper tracking, visitor logs, geolocation, segmentation and everything else depending on the real client IP.