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)

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-dynamic strategy, 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 in script-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.


Did this page help you?