Install a first-party collector (server-side)

Administration

Available for +16.0.0

Enterprise plan

Tag Manager

Needed permissions: owner or manage

A first-party collector lets you collect data 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 endpoint 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 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.
  • Learn more about the differences between client-side and server-side tracking. Read more 
  • The first-party collector is only available for Enterprise plans and for versions +16.0.0.

Add a site or app in Piwik PRO

If you haven’t created a site or app in Piwik PRO yet, 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 then 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 (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 for 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)

    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 Apache main configuration file:  

    # Modules required by Piwik PRO 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>
    
    # Piwik PRO reverse proxy definitions
    <IfModule proxy_http_module>
        ProxyPass "/2aecbbf86c74c1c2fb798e0a39f0678e.js" "https://accountname.piwik.pro/ppms.js"
        ProxyPassReverse "/2aecbbf86c74c1c2fb798e0a39f0678e.js" "https://accountname.piwik.pro/ppms.js"
    
        ProxyPass "/2aecbbf86c74c1c2fb798e0a39f0678e" "https://accountname.piwik.pro/ppms.php"
        ProxyPassReverse "/2aecbbf86c74c1c2fb798e0a39f0678e" "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/"
    </IfModule>
    

    Apache reverse proxy (method 2)

    First, enable mod_proxy 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
        RewriteBase /
        RewriteRule ^2aecbbf86c74c1c2fb798e0a39f0678e\.js(.*)$ https://accountname.piwik.pro/ppms.js$1 [P]
        RewriteRule ^2aecbbf86c74c1c2fb798e0a39f0678e(.*)$ 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]
    </IfModule>
    

    Nginx reverse proxy

    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 Nginx main configuration file:

    http {
        [...]
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Host $host;
    
        server {
          [...]
            location /2aecbbf86c74c1c2fb798e0a39f0678e.js {
                proxy_pass https://accountname.piwik.pro/ppms.js;
            }
    
            location /2aecbbf86c74c1c2fb798e0a39f0678e {
                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/;
            }
        }
    }
    

    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>/2aecbbf86c74c1c2fb798e0a39f0678e.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 When visitors don’t consent > Collect data using a 30-minute cookie (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);
       })('/2aecbbf86c74c1c2fb798e0a39f0678e','/2aecbbf86c74c1c2fb798e0a39f0678e.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 When visitors don’t consent > Collect data without using 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(['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);
       })('/2aecbbf86c74c1c2fb798e0a39f0678e','/2aecbbf86c74c1c2fb798e0a39f0678e.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);
       })('/2aecbbf86c74c1c2fb798e0a39f0678e','/2aecbbf86c74c1c2fb798e0a39f0678e.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 some questions, visit our community.
There’s always someone ready to help!

Back to help center