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 Data Activation API and support the Piwik PRO site inspector extension (e.g. admin.organization.piwik.pro)
Nonce placeholder
INSERT_VALID_NONCE_VALUE- the server-generated nonce value, unique for each request
Strategies
Piwik PRO supports two Content Security Policy script-src strategies:
strict-dynamic strategy (recommended)
strict-dynamic strategy (recommended)A modern, recommended approach that uses a nonce in the Piwik PRO installation code. The browser then treats scripts loaded by that code, including Tag Manager tags, as trusted. These scripts can run without additional nonces or Piwik PRO domains inscript-src.
Nonce-only strategy
This strategy passes nonce to every script, and lists Piwik PRO domains in script-src. This requires you to keep CSP rules in sync with the tracking setup. To allow a script to run, add a nonce to its element, add its domain to the policy, or do both.
Setup
The following sections describe a standard installation that uses Piwik PRO Tag Manager. The optional extensions cover additional Piwik PRO features.
Basic setup
script-src
strict-dynamic strategy:
script-src 'nonce-INSERT_VALID_NONCE_VALUE' 'strict-dynamic';
Nonce-only strategy:
script-src https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE 'nonce-INSERT_VALID_NONCE_VALUE';
Tracking
These directives cover Tag Manager container loading and data collection with the JavaScript Tracking Client (JSTC):
connect-src https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE https://INSERT_TRACKING_DOMAIN_VALUE;
img-src https://INSERT_TRACKING_DOMAIN_VALUE;
Installation code
Add the nonce to the installation code’s <script> tag or to pass it to your framework SDK. Use the same framework integration for both strategies.
<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,
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>If you use the nonce-only strategy, add tags.nonce="INSERT_VALID_NONCE_VALUE", to the line that sets tags.async:
tags.async=!0,
tags.nonce="INSERT_VALID_NONCE_VALUE",
tags.src="...Framework SDKs
import PiwikPro from '@piwikpro/react-piwik-pro';
PiwikPro.initialize('container-id', 'container-url', { nonce: 'nonce-string' });
ReactDOM.render(<App />, document.getElementById('root'))import PiwikProProvider from '@piwikpro/next-piwik-pro'
export default function RootLayout({
children
}: {
children: React.ReactNode
}) {
return (
<html lang='en'>
<body>
<PiwikProProvider
containerId='container-id'
containerUrl='container-url'
nonce='nonce-string'
// ^^^^^^^^^^^^^^^^^^^^
>
{children}
</PiwikProProvider>
</body>
</html>
)
}// gatsby-config.js
plugins: [
{
resolve: '@piwikpro/gatsby-plugin-piwik-pro',
options: {
pluginEnabled: true,
containerUrl: 'https://example.containers.piwik.pro/',
containerId: 'dc0f2c80-79d8-456a-9c77-6d48d6f867dd',
nonceString: 'nonce-string',
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
},
},
]import { NgxPiwikProModule } from "@piwikpro/ngx-piwik-pro";
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
NgxPiwikProModule.forRoot("container-id", "container-url",
{ nonce: "nonce-string" }
// ^^^^^^^^^^^^^^^^^^^^^^^^^
),
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}import VuePiwikPro from '@piwikpro/vue-piwik-pro'
VuePiwikPro.initialize("container-id", "container-url",
{ nonce: 'nonce-string' }
// ^^^^^^^^^^^^^^^^^^^^^^^^^
)// nuxt.config.ts
export default defineNuxtConfig({
modules: [
[
"@piwikpro/nuxt-piwik-pro",
{
containerId: "container-id",
containerUrl: "container-url",
nonce: "nonce-string"
// ^^^^^^^^^^^^^^^^^^^^^
},
],
],
//...
});Custom tags
Tag Manager adds the nonce to <script> elements in a custom tag's code. However, if the tag's JavaScript creates additional <script> elements at runtime, Tag Manager doesn’t add the nonce to them automatically.
- If you use the
strict-dynamicstrategy, you don't need to take any additional action. - If you use the nonce-only strategy, add a nonce to each runtime
<script>or allowlist its URL inscript-src.
Optional extensions
Tag Manager styles
If you use tags that add <style> elements into the page, allow style with a nonce:
style-src 'nonce-INSERT_VALID_NONCE_VALUE';
Tag Manager debug mode
If you use Tag Manager debug mode, include a nonce in style-src and allow fonts from Piwik PRO servers. The debug widget loads its font files from these servers:
style-src 'nonce-INSERT_VALID_NONCE_VALUE';
font-src https://INSERT_UI_APIS_DOMAIN_VALUE;
Consent Manager (legacy) form assets
If Consent Manager (legacy) is enabled on your website:
style-src 'nonce-INSERT_VALID_NONCE_VALUE';
connect-src https://INSERT_TRACKING_DOMAIN_VALUE https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE;
Consent Manager (legacy) data subject request widget
If you use a data subject request widget, add a nonce attribute to its <script> tag:
<div id="ppms_cm_data_subject" ...>
<h3 ...>...</h3>
<form ...>...</form>
<script nonce="INSERT_VALID_NONCE_VALUE">
...
</script>
</div>Piwik PRO site inspector
If you use the site inspector, add the following URLs to your connect-src and frame-src directives:
connect-src https://INSERT_UI_APIS_DOMAIN_VALUE/api/;
frame-src https://INSERT_UI_APIS_DOMAIN_VALUE/site-inspector/;
Example policies
The following examples use these values:
- Static resources domain:
organization.containers.piwik.pro - Tracking domain:
organization.piwik.pro - UI and APIs domain:
admin.organization.piwik.pro - Nonce:
nceIOfn39fn3e9h3sd
Minimal
Basic setup only. No optional extensions.
strict-dynamic strategy:
default-src 'none';
script-src 'self' 'nonce-nceIOfn39fn3e9h3sd' 'strict-dynamic';
connect-src 'self' https://organization.containers.piwik.pro https://organization.piwik.pro;
img-src 'self' https://organization.piwik.pro;
Nonce-only strategy:
default-src 'none';
script-src 'self' https://organization.containers.piwik.pro 'nonce-nceIOfn39fn3e9h3sd';
connect-src 'self' https://organization.containers.piwik.pro https://organization.piwik.pro;
img-src 'self' https://organization.piwik.pro;
Full
This configuration includes the Basic setup and all optional extensions: Tag Manager styles, Tag Manager debug mode, Consent Manager (legacy) form assets, Consent Manager (legacy) data subject request widget, and Piwik PRO site inspector.
Add a nonce to the data subject request widget’s<script> tag in HTML. You don't need to add any header directives beyond script-src.
strict-dynamic strategy:
default-src 'none';
script-src 'self' 'nonce-nceIOfn39fn3e9h3sd' 'strict-dynamic';
connect-src 'self' https://organization.containers.piwik.pro https://organization.piwik.pro https://admin.organization.piwik.pro/api/;
img-src 'self' https://organization.piwik.pro;
style-src 'self' 'nonce-nceIOfn39fn3e9h3sd';
font-src 'self' https://admin.organization.piwik.pro;
frame-src 'self' https://admin.organization.piwik.pro/site-inspector/;
Nonce-only strategy:
default-src 'none';
script-src 'self' https://organization.containers.piwik.pro 'nonce-nceIOfn39fn3e9h3sd';
connect-src 'self' https://organization.containers.piwik.pro https://organization.piwik.pro https://admin.organization.piwik.pro/api/;
img-src 'self' https://organization.piwik.pro;
style-src 'self' 'nonce-nceIOfn39fn3e9h3sd';
font-src 'self' https://admin.organization.piwik.pro;
frame-src 'self' https://admin.organization.piwik.pro/site-inspector/;
JavaScript Tracking Client (JSTC) without Tag Manager
Use this setup when you load JSTC directly on the page without the Tag Manager installation code.
img-src https://INSERT_TRACKING_DOMAIN_VALUE/ppms.php;
connect-src https://INSERT_TRACKING_DOMAIN_VALUE/ppms.php;
script-src https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE/ppms.js;
Note: You may need to adjust these paths if you don't use the default settings. For example, you can load an alternative JavaScript Tracking Client (JSTC) build that doesn't conflict with the Matomo script:
https://INSERT_STATIC_RESOURCES_DOMAIN_VALUE/ppas.js.
Updated about 12 hours ago