This script allows to track websites with Matomo without revealing the Matomo server URL.
This is useful for users who track multiple websites on the same Matomo server, but don't want to show the Matomo server URL in the source code of all tracked websites.
To run this properly you will need:
- latest version of Matomo installed on a server (or Matomo Cloud)
- one or several website(s) to track with this Matomo, for example
http://{site_to_be_tracked} - the website to track must run on a server with PHP 7.2 or higher
- PHP must have either the CURL extension enabled or
allow_url_fopen=On
In your Matomo server:
- login as Super user
- create a user, set the login for example: "UserTrackingAPI"
- assign this user write or admin permission on all websites you wish to track (otherwise the visitor IP address and other things won't be tracked correctly)
- log in as this newly created user and create an auth token
You need to install the proxy on the server where your websites are hosted. You can do it both ways:
- download the files manually
- or install the whole repository with git
| ⚡ Important note about where to install the proxy |
|---|
To ensure the highest data accuracy possible, and that your Matomo cookies are set correctly, please install the proxy in your main website domain name and web server. This proxy should be ideally installed on your webserver directly under {site_to_be_tracked}. If you installed the proxy in a sub-domain under analytics.{site_to_be_tracked} then this would cause data to be less accurate. (Why? because if the sub-domain analytics.{site_to_be_tracked} was to resolve to a CNAME that does not match {site_to_be_tracked} OR if it was to resolve to A/AAAA addresses that do not match the first half of the A/AAAA addresses running {site_to_be_tracked}, then the cookies set by the Matomo Tracker Proxy in the response would only have a lifetime of maximum 7 days on Safari >= 16.4.) |
- download
matomo.php, downloadpiwik.php,proxy.php,matomo-proxy.php& if you are using the Heatmaps and Session recordings plugin also downloadplugins/HeatmapSessionRecording/configs.phpto your website root directory, for example athttp://{site_to_be_tracked}/matomo.php,http://{site_to_be_tracked}/piwik.php,http://{site_to_be_tracked}/proxy.php,http://{site_to_be_tracked}/matomo-proxy.php&http://{site_to_be_tracked}/plugins/HeatmapSessionRecording/configs.php - edit the file to set the configuration variables:
$MATOMO_URLshould contain the URL to your Matomo server$PROXY_URLshould contain the URL to the tracker-proxy server$TOKEN_AUTHshould contain thetoken_auth
- clone the repository:
git clone https://github.com/matomo-org/tracker-proxy.git matomointo your website root directory (for example athttp://{site_to_be_tracked}/matomo/matomo.php) - copy the configuration template:
cp config.php.example config.php - change the configuration in the newly created
config.php:$MATOMO_URLshould contain the URL to your Matomo server$PROXY_URLshould contain the URL to the tracker-proxy server$TOKEN_AUTHshould contain thetoken_auth
By using git you will later be able to update by simply running git pull.
Be aware that with this method, matomo.php and other files are in a matomo/ subdirectory. Keep that in mind when applying the instructions for the next step.
The proxy file (http://{site_to_be_tracked}/matomo.php) will be called by the Matomo Javascript tracker instead of calling directly the (secret) Matomo server (http://your-matomo-domain.example.org/matomo/).
To achieve this, change the Matomo Javascript Code that is in the footer of your pages:
-
go to Matomo > Settings > Websites > Show Javascript Tracking Code.
-
copy the Javascript snippet and change the last lines to the following:
[...] (function() { var u="//{site_to_be_tracked}/"; _paq.push(["setTrackerUrl", u+"matomo.php"]); _paq.push(["setSiteId", "tracked-site-id-here"]); var d=document, g=d.createElement("script"), s=d.getElementsByTagName("script")[0]; g.type="text/javascript"; g.async=true; g.defer=true; g.src=u+"matomo.php"; s.parentNode.insertBefore(g,s); })(); </script> <!-- End Matomo Code -->
What has changed in this code snippet compared to the normal Matomo code?
- any reference(s) to the secret Matomo URL are now replaced by your website URL (the proxy).
matomo.jsbecomesmatomo.php(ormatomo/matomo.phpif you used the git method): matomo.php is the proxy script- make sure to replace
tracked-site-id-herewith your idsite - if the
<noscript>is present in your tracking code, you can remove it (it contains the secret Matomo URL which you want to hide)
-
paste the modified Matomo Javascript code in the pages you wish to track.
This modified Javascript code will then track visits/pages/conversions by calling {site_to_be_tracked}/matomo.php, which will then automatically call your (hidden) Matomo Server URL.
At this stage, example.com should be tracked by your Matomo without showing the Matomo server URL. Repeat the step 3. for each website you wish to track in Matomo.
The tracker-proxy also supports proxying the matomo opt out form. To use this, change the URL used in the opt out iframe to use
the tracker proxy's matomo-proxy.php file.
Note: you can get the opt out iframe from inside the Administration > Privacy > Users opt-out page in your Matomo instance.
By default, the matomo.php proxy will wait 5 seconds for the Matomo server to return the response.
You may change this timeout by editing the $timeout value in config.php.
By default, the matomo.php proxy will contact your Matomo server with the User-Agent of the client requesting matomo.php.
You may force the proxy script to use a particular User-Agent by editing the $user_agent value in config.php.
Because the proxy sits between your visitors and Matomo, it has to tell Matomo the real visitor IP — otherwise Matomo would record the proxy's IP. There are two ways this works:
- Default — via
cip+token_auth: the proxy sends the visitor IP to Matomo as theciptracking parameter, authorized by the$TOKEN_AUTHyou configured (this is why the proxy user needs write or admin permission). Works out of the box with no Matomo-side configuration, for both single requests and bulk requests (the Matomo JavaScript tracker batches several actions into a single bulk request by default). - Header-only — via
$http_ip_forward_header: set$http_ip_forward_headerinconfig.php(for example toX-Forwarded-For) to forward the visitor IP in that header instead. In this mode the proxy injects nocip/token_authat all and relies solely on the header for the visitor IP — so it doesn't even need a write/admin token. This only works if Matomo is configured to trust the header: both the web server in front of Matomo (Apache mod_remoteip, nginx realip) and Matomo's trusted-proxy settings (proxy_client_headers[]/proxy_ips[]in itsconfig.ini.php). If it isn't, Matomo records the proxy's IP for every visitor.
⚠️ Breaking change: previously$http_ip_forward_headerwas sent in addition tocip+token_auth; the proxy now treats it as the sole IP mechanism and injects nothing else. If you already set it, make sure Matomo's trusted-proxy configuration above is in place — otherwise leave it empty to keep usingcip.
Some tracking parameters (cip, cdt, cdo, country, region, city, lat, long) are only honored by Matomo for an authenticated request. The proxy never lends its $TOKEN_AUTH to a request — or to an individual entry of a bulk request — that carries one of these override parameters or its own token_auth:
- Carries an override parameter, no token: forwarded without the proxy's token, so Matomo rejects/skips it exactly as if it had been sent directly without authentication — rather than being silently tracked with the client-supplied override. To set these parameters legitimately, send your own valid
token_auth. - Carries its own
token_auth: the proxy adds no token of its own and lets the client's token govern. It still forwards the visitor IP ascip, so that token must have write access to authorize it (otherwise the request/entry is rejected).
⚠️ Behavior change: if you add any of these parameters viaappendToTrackingUrl(or otherwise) without your owntoken_auth, those requests are now rejected by Matomo. Previously the proxy stripped the parameter and tracked the rest of the hit; it no longer does. Send a validtoken_authif you need these parameters.
Note: if your Matomo server sets
bulk_requests_require_authentication = 1, it requires a single batch-leveltoken_authfor the whole bulk request. The proxy supplies that batch-level token only when every entry in the batch is clean; if any entry carries an override parameter or its owntoken_auth, the proxy withholds it (a batch-level token would wrongly authorize that entry) and Matomo then rejects the entire bulk request — clean entries included. Under that configuration, send your own batch-leveltoken_author avoid mixing override entries into proxied bulk requests.
Note: bulk tracking requests must be sent with an
application/x-www-form-urlencodedcontent type (as the Matomo JavaScript tracker does). Bulk request bodies sent asapplication/jsonare not forwarded by the proxy.
Note: the proxy rebuilds the forwarded request from the parameters PHP parsed (
$_GET/$_POST, or the decoded JSON for bulk), so it only ever sends Matomo what it inspected. Make sure the proxy host's PHPpost_max_sizeis large enough for your biggest (bulk) tracking requests — a body exceeding it is dropped by PHP and not forwarded.
If you have found a bug, you are welcome to submit a pull request.
Before running the tests, create a config.php file w/ the following contents in the root repository directory:
<?php
$MATOMO_URL = 'http://localhost/tests/server/';
$PROXY_URL = 'http://localhost/';
$TOKEN_AUTH = 'xyz';
$timeout = 5;
// Test-only: lets the suite exercise IP-forward-header handling via the X-Test-Ip-Forward-Header
// request header. Gated on the local test-server URL so a stray copy to production is inert.
if (strpos($MATOMO_URL, '/tests/server/') !== false && !empty($_SERVER['HTTP_X_TEST_IP_FORWARD_HEADER'])) {
$http_ip_forward_header = $_SERVER['HTTP_X_TEST_IP_FORWARD_HEADER'];
}
The tests need a webserver to be pointed to the root of this repository. The simplest way is to just use Vagrant:
$ vagrant up
$ vagrant ssh
$ cd /vagrant/tests
$ composer install
- Set
allow_url_fopen = Offin your webserver php.ini - Check in phpinfo() that
allow_url_fopen = Off - Run:
vendor/bin/phpunit
- Set
allow_url_fopen = Onin your webserver php.ini - Check in phpinfo() that
allow_url_fopen = On - Run:
vendor/bin/phpunit
Be advised that the proxy and its tests require PHP 7.2 or higher.