Content Security Policy (CSP)
Introduction
Specifying a Content Security Policy (CSP) is a common way to secure web applications. This policy controls which scripts and styles can run on a page. You can implement it by adding a Content-Security-Policy header or using a meta tag.
Value placeholders
In this guide, we use placeholders that you’ll need to replace to ensure your Content Security Policy works correctly and stays in sync with your application's settings.
Note: All placeholders start with
INSERT_
prefix
Domain placeholders
You can find the domain values to replace in Administration > Sites & apps > Your site or app > Data collection > Domains setup.
INSERT_STATIC_RESOURCES_DOMAIN_VALUE
- the domain used for fetching tracking scripts and consent form assets (e.g. organization.containers.piwik.pro)INSERT_TRACKING_DOMAIN_VALUE
- the domain used for tracking visitors and collecting visitor consents (e.g. organization.piwik.pro)INSERT_UI_APIS_DOMAIN_VALUE
- the domain used to fetch visitor's audiences from the CDP API and support the Site Inspector extension (e.g. organization.piwik.pro)
Nonce placeholder
INSERT_VALID_NONCE_VALUE
- the server-generated nonce value, unique for each request
Content Security Policy nonce setup
It's common practice to allow only scripts and styles from trusted domains or those with a special nonce attribute. The nonce mechanism involves two steps: defining the nonce value in the Content Security Policy and adding the same nonce value as an attribute in the styles and scripts.
Define a nonce in Content Security Policy settings
The nonce mechanism requires an additional definition in the script-src directive of the Content Security Policy:
script-src <your-sources> 'nonce-INSERT_VALID_NONCE_VALUE';
Note:
<your-sources>
is a placeholder for your permitted sources.
Add the nonce to the container code
As a result, the default container code requires the following modifications to function properly.
Installation code: The following changes are required in this container code:
<script type="text/javascript" nonce="INSERT_VALID_NONCE_VALUE">
(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');
var qP=[];dataLayerName!=="dataLayer"&&qP.push("data_layer_name="+dataLayerName);var qPString=qP.length>0?("?"+qP.join("&")):"";
tags.async=!0,tags.src="https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE/"+id+".js"+qPString,tags.nonce="INSERT_VALID_NONCE_VALUE",
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', 'feacd61d-0232-40a1-96c3-7e469f7bfa7f');
</script>
Adjusting tags to work with the Content Security Policy
In most cases, no changes are needed to make the tags work. Tag Manager will automatically insert the nonce attribute into all fired tags. The only exception is when your tag adds additional scripts or styles to the page; in such cases, you should manually add the nonce attribute.
Note: Not all third-party tools available as built-in templates are configured to work with Content Security Policy. This includes tools like Google Analytics. In these cases, refer to the documentation for each tool (e.g., https://developers.google.com/web/fundamentals/security/csp).
Consent Manager form assets
If Consent Manager is enabled on your website, you'll need to set the connect-src
, style-src
and img-src
directives:
connect-src <your-sources> INSERT_TRACKING_DOMAIN_VALUE INSERT_STATIC_RESOURCES_DOMAIN_VALUE;
style-src <your-sources> 'nonce-INSERT_VALID_NONCE_VALUE';
Consent Manager’s data subject request widget
When using a data subject request widget, you need to add a nonce attribute to its <script>
tag.
<div id="ppms_cm_data_subject" class="ppms_cm_data_subject_widget__wrapper" data-editor-centralize="true" data-main-container="true" data-root="true">
<h3 id="ppms_cm_data_subject_header" class="header3">Data requests</h3>
<p id="ppms_cm_data_subject_paragraph" class="paragraph">
Please select below the type of data request along with any special requests in the body of the message. (...)
</p>
<form id="ppms_cm_data_subject_form" class="ppms_cm_data_subject_form" data-disable-select="true">
...
</form>
<script nonce="INSERT_VALID_NONCE_VALUE">
...
</script>
</div>
JavaScript tracking client
To load all the necessary assets for the JavaScript tracking client, define the sources for script-src
, img-src
and connect-src
.
script-src <your-sources> https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE/ppms.js;
img-src <your-sources> https://INSERT_TRACKING_DOMAIN_VALUE/ppms.php;
connect-src <your-sources> https://INSERT_TRACKING_DOMAIN_VALUE/ppms.php;
Note: You may need to adjust these paths if you're not using the default settings. For example, the JavaScript library might be loaded as an alternative build that doesn't conflict with the Matomo script:
https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE/ppas.js
.
Allowing the site inspector extension to work on the site
Our site inspector is a Chrome browser extension that visualizes analytics data like clickmaps, heatmaps and scrollmaps on tracked pages. By default, the JavaScript tracking client adds the necessary configuration for this extension in the page's HTML. However, you can disable this behavior if needed using setSiteInspectorSetup.
Since the extension integrates with the page it runs on, additional CSP rules are required to ensure it functions properly.
connect-src <your-sources+necessary-Piwik-PRO-sources> https://INSERT_UI_APIS_DOMAIN_VALUE/api/;
frame-src <your-sources+necessary-Piwik-PRO-sources> https://INSERT_UI_APIS_DOMAIN_VALUE/site-inspector/;
Note: If you're using a custom domain for tracking, make sure to adjust the domain in the CSP rules accordingly.
Example Content Security Policy definition
The following example Content Security Policy setup assumes:
- Your website address: client.com
- Your container domain: INSERT_STATIC_RESOURCES_DOMAIN_VALUE
- You have a Piwik PRO tag with the default tracking domain: INSERT_TRACKING_DOMAIN_VALUE
- Nonce value: nceIOfn39fn3e9h3sd
- The setup allows the
'self'
source which is client.com
Content-Security-Policy: default-src 'self';
script-src 'self' INSERT_TRACKING_DOMAIN_VALUE 'nonce-nceIOfn39fn3e9h3sd';
connect-src 'self' INSERT_STATIC_RESOURCES_DOMAIN_VALUE INSERT_TRACKING_DOMAIN_VALUE;
img-src 'self' INSERT_STATIC_RESOURCES_DOMAIN_VALUE INSERT_TRACKING_DOMAIN_VALUE;
Updated about 13 hours ago