Install a first-party collector (hybrid server-side)

Enterprise plan

Available for +16.0.0

Administration

Tag Manager

Needed permissions: owner or manage

A first-party collector lets you collect data hybrid server-side. This method sends data from a visitor’s browser to your own server and then to your Piwik PRO account using a reverse proxy. With this approach, you are able to (1) have the same tracking domain as your website, (2) optionally use subdomains as tracking endpoints and (3) have the first-party tracking solution.

In this article, we’ll walk you through the process of adding a site or app in Piwik PRO and setting up the first-party collector.

Before you start

Here are some key things to know before you begin the process:

  • If you use both Matomo and Piwik PRO, make sure that there’s no conflict between the tracking codes. Read more
  • You’ll need to repeat the reverse proxy setup for each site you want to track with the first-party collector – even if you use the same tracking code for a few sites. The same reverse proxy setup can’t be used for multiple sites.
  • You’ll need to update your Content Security Policy (CSP) configuration if you use CSP. After setting up the first-party collector, you may be able to remove some Piwik PRO domains from the CSP allowlist.
  • Installing the first-party collector will cause the following paths on your website to be forwarded to Piwik PRO: /t.js, /t.php, /containers/, /consent/ and /api/cdp/. Make sure your website is not using them before installation. If it does, it may lead to issues on the site. You can modify the paths according to your needs. If you have any doubts about how to do this, contact us.
  • Learn more about the differences between client-side and hybrid server-side tracking. Read more 
  • The first-party collector is only available in Enterprise plans and versions +16.0.0.

Add a site or app in Piwik PRO

If you haven’t yet created a site or app in Piwik PRO, this is the step you need to take before installing the first-party collector.

To add a site or app in Piwik PRO, follow these steps:

  1. Go to Menu > Administration.
  2. Navigate to Sites & apps.
  3. Click Add a site or app.
  4. Type the site or app name and address and click Save.
    Site or app address (Administration)

    Note: If you want to track a few domains with the same tracking code, add their addresses. Just keep in mind that you’ll need to add these domains to your custom tracking code and set up a reverse proxy for each domain separately.

  5. Set the time zone and currency.
    Set the time zone and currency

    Note: We’ll show data in reports in this time zone. We’ll use this currency for goal revenue.

  6. That’s done! Now let’s move on to setting up the first-party collector.

Install a first-party collector (hybrid server-side)

To collect your web data using our first-party collector, follow these steps:

  1. Allowlist your reverse proxy using one of the following options:
    • Contact our support at support@piwik.pro and ask for the key to a reverse proxy. Then set the key as the X-Proxy-Auth-Sign header in your reverse proxy setup. (Available in private and public cloud accounts on Azure)
    • Contact our support at support@piwik.pro and send them all IP addresses used by your proxy. We’ll allowlist it for you.
  2. Set up your reverse proxy in Apache or Nginx.

    Apache reverse proxy (method 1)

    First, enable the following modules: proxyproxy_httpheadersssl and set SSLProxyEngine on.

    In the code below, replace accountname.piwik.pro with your account name, piwikpro-reverse-proxy-key with the token you’ve received from us and add the code to your Apache main configuration file:

    # Modules required by PiwikPRO reverse proxy
      <IfModule !proxy_module>
          LoadModule proxy_module modules/mod_proxy.so
      </IfModule>
      <IfModule !proxy_http_module>
          LoadModule proxy_http_module modules/mod_proxy_http.so
      </IfModule>
      <IfModule !headers_module>
          LoadModule headers_module modules/mod_headers.so
      </IfModule>
      <IfModule !ssl_module>
          LoadModule ssl_module modules/mod_ssl.so
      </IfModule>
    
      # PiwikPRO reverse proxy definitions
      <IfModule proxy_http_module>
          ProxyPass "/t.js" "https://accountname.piwik.pro/ppms.js"
          ProxyPassReverse "/t.js" "https://accountname.piwik.pro/ppms.js"
    
          ProxyPass "/t.php" "https://accountname.piwik.pro/ppms.php"
          ProxyPassReverse "/t.php" "https://accountname.piwik.pro/ppms.php"
    
          ProxyPass "/containers/" "https://accountname.piwik.pro/containers/"
          ProxyPassReverse "/containers/" "https://accountname.piwik.pro/containers/"
    
          ProxyPass "/consent/" "https://accountname.piwik.pro/consent/"
          ProxyPassReverse "/consent/" "https://accountname.piwik.pro/consent/"
    
          ProxyPass "/api/cdp/" "https://accountname.piwik.pro/api/cdp/"
          ProxyPassReverse "/api/cdp/" "https://accountname.piwik.pro/api/cdp/"
    
          RequestHeader set X-Proxy-Auth-Sign "piwikpro-reverse-proxy-key"
      </IfModule>
    

    Apache reverse proxy (method 2)

    First, enable the following modules: proxyproxy_httpheadersssl , rewrite and set SSLProxyEngine on.

    In the code below, replace accountname.piwik.pro with your account name, add the reverse proxy key (if you use it to allowlist your reverse proxy) and add the code to your .htaccess file:

    <IfModule mod_rewrite.c>
          RewriteEngine On
          <IfModule  mod_proxy_http.c>
              <IfModule mod_ssl.c>
                  RewriteBase /
                  RewriteRule ^t\.js($|\?.*)$ https://accountname.piwik.pro/ppms.js$1 [P]
                  RewriteRule ^t\.php($|\?.*)$ https://accountname.piwik.pro/ppms.php$1 [P]
                  RewriteRule ^containers/(.*)$ https://accountname.piwik.pro/containers/$1 [P]
                  RewriteRule ^consent/(.*)$ https://accountname.piwik.pro/consent/$1 [P]
                  RewriteRule ^api/cdp/(.*)$ https://accountname.piwik.pro/api/cdp/$1 [P]
              </IfModule>
          </IfModule>
    
          <IfModule mod_headers.c>
              RequestHeader set X-Proxy-Auth-Sign "piwikpro-reverse-proxy-key"
          </IfModule>
      </IfModule>

    Nginx reverse proxy

    In the code below, replace accountname.piwik.pro with your account name, piwikpro-reverse-proxy-key with the token you’ve received from us and add the code to your Nginx main configuration file:

    http {
          [...]
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Host $host;
          proxy_set_header X-Proxy-Auth-Sign piwikpro-reverse-proxy-key;
          server {
            [...]
              location /t.js {
                  proxy_pass https://accountname.piwik.pro/ppms.js;
              }
    
              location /t.php {
                  proxy_pass https://accountname.piwik.pro/ppms.php;
              }
    
              location /containers/ {
                  proxy_pass https://accountname.piwik.pro/containers/;
              }
    
              location /consent/ {
                  proxy_pass https://accountname.piwik.pro/consent/;
              }
    
              location /api/cdp/ {
                  proxy_pass https://accountname.piwik.pro/api/cdp/;
              }
          }
      }
    

    Note: Repeat the reverse proxy setup (steps 1–2) for each site you want to track with the first-party collector – even if you use the same tracking code for a few sites. The same reverse proxy setup can’t be used for multiple sites.

  3. Test your setup by going to one of these endpoints:
    • https://<yourwebsitedomain>/containers/<site-app-ID>.js
    • https://<yourwebsitedomain>/t.js

    They should return JavaScript code.

  4.  Go to Piwik PRO > Menu > Tag Manager > Tags and turn off Piwik PRO tag (tracking code). In the next steps, you’ll set up custom tracking.
  5. Set up your custom tracking depending on Consent Manager settings:

    (Case 1) If Menu > Administration > Sites & apps > Privacy > Ask visitors for consent (on) and Collect anonymous data from non-consenting visitors (on) > Use visitor cookies (on)

    In the tracking code below, replace <site-app-ID> with your site ID (Where to find it?) and add the code as:

    • Tag type: Custom code
    • Consent type: No consent is required
    • Trigger: All page views
    <script>
       var _paq = window._paq || [];
    
    
    (function () {
           var trackAnonymously = {{ Piwik PRO Anonymization }}('analytics');
           if (trackAnonymously) {
               _paq.push(['setUserIsAnonymous', 1]);
               _paq.push(['setVisitorCookieTimeout', 1800]);
           }
       }());
    
    
       _paq.push(['setTrackingSource', 'jstc_tm']);
       _paq.push(['enableLinkTracking']);
       _paq.push(['setDomains', ['www.example.com', 'shop.example.com']]); // Add this line only if you use this tag on more than one domain
       _paq.push(['setCookieDomain', '.example.com']); // Add this line only if you have cross-subdomain tracking
       _paq.push(['setSecureCookie', 1]);
       _paq.push(['enableHeartBeatTimer']);
       _paq.push(['trackPageView']);
       _paq.push(['enableJSErrorTracking']);
       (function(p,i,w,ik) {
           var g=ik.createElement('script'),s=ik.getElementsByTagName('script')[0];
           _paq.push(['setTrackerUrl', p]);
           _paq.push(['setSiteId', w]);
           g.type='text/javascript';g.async=true;g.defer=true;g.src=i;s.parentNode.insertBefore(g,s);
       })('/t.php','/t.js','<site-app-ID>',document)
    </script>
    

    Add this deanonymization code as:

    • Tag type: Custom code
    • Consent type: Analytics
    • Trigger: All page views
    <script>
       var _paq = window._paq || [];
       (function () {
           var consents = {{ Consents }};
           var consentType = 'analytics';
           
           if (typeof consents !== 'undefined' &&
               consents.hasOwnProperty('current_state') &&
               consents.current_state.hasOwnProperty(consentType) &&
               consents.current_state[consentType] === 1 &&
               consents.hasOwnProperty('previous_state') &&
               consents.previous_state.hasOwnProperty(consentType) &&
               consents.previous_state[consentType] !== 1
           ) {
    		   _paq.push(['deanonymizeUser']);
       		   _paq.push(['setVisitorCookieTimeout', 33955200]);
           }
       }());
    </script>

    (Case 2) If Menu > Administration > Sites & apps > Privacy > Ask visitors for consent (on) and Collect anonymous data from non-consenting visitors (on) > Use a session hash (on)

    In the tracking code below, replace <site-app-ID> with your site ID (Where to find it?) and add the code as:

    • Tag type: Custom code
    • Consent type: No consent is required
    • Trigger: All page views
    <script>
       var _paq = window._paq || [];
    
    
    (function () {
           var trackAnonymously = {{ Piwik PRO Anonymization }}('analytics');
           if (trackAnonymously) {
               _paq.push(['setUserIsAnonymous', 1]);
               _paq.push(['disableCookies']);
           }
       }());
    
    
       _paq.push(['setTrackingSource', 'jstc_tm']);
       _paq.push(['enableLinkTracking']);
       _paq.push(['setDomains', ['www.example.com', 'shop.example.com']]); // Add this line only if you use this tag on more than one domain
       _paq.push(['setCookieDomain', '.example.com']); // Add this line only if you have cross-subdomain tracking
       _paq.push(['setSecureCookie', 1]);
       _paq.push(['enableHeartBeatTimer']);
       _paq.push(['trackPageView']);
       _paq.push(['enableJSErrorTracking']);
       (function(p,i,w,ik) {
           var g=ik.createElement('script'),s=ik.getElementsByTagName('script')[0];
           _paq.push(['setTrackerUrl', p]);
           _paq.push(['setSiteId', w]);
           g.type='text/javascript';g.async=true;g.defer=true;g.src=i;s.parentNode.insertBefore(g,s);
       })('/t.php','/t.js','<site-app-ID>',document)
    </script>
    

    Add this deanonymization code as:

    • Tag type: Custom code
    • Consent type: Analytics
    • Trigger: All page views
    <script>
       var _paq = window._paq || [];
       (function () {
           var consents = {{ Consents }};
           var consentType = 'analytics';
           
           if (typeof consents !== 'undefined' &&
               consents.hasOwnProperty('current_state') &&
               consents.current_state.hasOwnProperty(consentType) &&
               consents.current_state[consentType] === 1 &&
               consents.hasOwnProperty('previous_state') &&
               consents.previous_state.hasOwnProperty(consentType) &&
               consents.previous_state[consentType] !== 1
           ) {
               		   _paq.push(['deanonymizeUser']);
    		   _paq.push(['enableCookies']);
           }
       }());
    </script>

    (Case 3) If Menu > Administration > Sites & apps > Privacy > Ask visitors for consent (off)

    In the tracking code below, replace <site-app-ID> with your site ID (Where to find it?)

    • Tag type: Custom code
    • Consent type: No consent is required
    • Trigger: All page views
    <script>
       var _paq = window._paq || [];
    
    
       _paq.push(['setTrackingSource', 'jstc_tm']);
       _paq.push(['enableLinkTracking']);
       _paq.push(['setDomains', ['www.example.com', 'shop.example.com']]); // Add this line only if you use this tag on more than one domain
       _paq.push(['setCookieDomain', '.example.com']); // Add this line only if you have cross-subdomain tracking
       _paq.push(['setSecureCookie', 1]);
       _paq.push(['enableHeartBeatTimer']);
       _paq.push(['trackPageView']);
       _paq.push(['enableJSErrorTracking']);
       (function(p,i,w,ik) {
           var g=ik.createElement('script'),s=ik.getElementsByTagName('script')[0];
           _paq.push(['setTrackerUrl', p]);
           _paq.push(['setSiteId', w]);
           g.type='text/javascript';g.async=true;g.defer=true;g.src=i;s.parentNode.insertBefore(g,s);
       })('/t.php','/t.js','<site-app-ID>',document)
    </script>
  6. In the code below, replace <site-app-ID> with your site ID (Where to find it?). Copy the code and paste it right after the opening <body> tag on every page of your site. (If you had our container installed before, replace it with this code.)
    <script>
       (function(window, document, dataLayerName, id) {
           window[dataLayerName]=window[dataLayerName]||[],window[dataLayerName].push({start:(new Date).getTime(),event:"stg.start"});var scripts=document.getElementsByTagName('script')[0],tags=document.createElement('script');
           function stgCreateCookie(a,b,c){var d="";if(c){var e=new Date;e.setTime(e.getTime()+24*c*60*60*1e3),d="; expires="+e.toUTCString()}document.cookie=a+"="+b+d+"; path=/"}
           var isStgDebug=(window.location.href.match("stg_debug")||document.cookie.match("stg_debug"))&&!window.location.href.match("stg_disable_debug");stgCreateCookie("stg_debug",isStgDebug?1:"",isStgDebug?14:-1);
           var qP=[];dataLayerName!=="dataLayer"&&qP.push("data_layer_name="+dataLayerName),isStgDebug&&qP.push("stg_debug");var qPString=qP.length>0?("?"+qP.join("&")):"";
           tags.async=!0,tags.src="/containers/"+id+".js"+qPString,scripts.parentNode.insertBefore(tags,scripts);
           !function(a,n,i){a[n]=a[n]||{};for(var c=0;c<i.length;c++)!function(i){a[n][i]=a[n][i]||{},a[n][i].api=a[n][i].api||function(){var a=[].slice.call(arguments,0);"string"==typeof a[0]&&window[dataLayerName].push({event:n+"."+i+":"+a[0],parameters:[].slice.call(arguments,1)})}}(i[c])}(window,"ppms",["tm","cm"]);
       })(window, document, 'dataLayer', '<site-app-ID>');
    </script>
  7. Go to Menu > Tag Manager and publish all changes.
  8. Data will appear in reports in about an hour. Data in the tracker debugger will appear instantly.

    Tip: You can also check manually if the tracking works. Read more

  9. Job done! Your site is now being tracked by our first-party collector. If you want to track other sites this way, repeat steps 1-8 for each site.

Was this article helpful?

Technical support

If you still have any questions, visit our community.
There’s always someone happy to help!

Back to help center