DEV Community

Cover image for Using Google Tag Manager with a Content-Security-Policy
Matija Mrkaic
Matija Mrkaic

Posted on

Using Google Tag Manager with a Content-Security-Policy

Configuring Content-Security-Policy (CSP) and allowing Google Tag Manager (GTM) scripts can be split into two main parts:

  1. Setting GTMs standard tag types.
  2. Setting GTMs Custom HTML tag types.

The first part will be covered in short notes to provide a handy overview. However, the main concern of this article is the second part, as it is a bit more tricky to set.
Also, note that using unsafe-inline defeats the whole purpose of CSP, so that's not an option.

0) Nonce

The only practical approach for CSP-allowing is to use the unique server-generated nonce value, created either via an appropriate library or simply generating the proper random string. The same nonce value can be used for all scripts, but it must be uniquely generated for each client. For example, generating it in javascript could look like this:

const GENERATED_NONCE = crypto.randomBytes(16).toString("base64");
Enter fullscreen mode Exit fullscreen mode

Add the rule to CSP header and allow generated nonce:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-{GENERATED_NONCE}'" />
Enter fullscreen mode Exit fullscreen mode

1) Allowing GTM and it's standard tag types

This part is fairly simple and nicely documented in

Outlined main steps are:

  1. Whitelist nonce in the CSP header (already done in the previous section of this article).
  2. Use nonce-aware version of GTM snippet - it will propagate the nonce to its scripts.
  3. Whitelist necessary resources in the CSP header for the tags used (just follow errors in the console).

In some sense, this should be the end of this article, but unfortunately, GTM doesn't propagate the nonce to any Custom HTML tags.

2) Setting Custom HTML tag types

Getting the nonce variable in GTM

In order to add the nonce attribute to the Custom HTML scripts, it must be first defined as a GTM variable:

  1. Add id="gtmScript" to the nonce-aware version of GTM snippet - this will be used to target the element and capture nonce.
<script id="gtmScript" nonce="{GENERATED_NONCE}">
  // GTM function
Enter fullscreen mode Exit fullscreen mode
  1. In GTM, create a new variable that will capture the nonce. Use DOM Element type, and select the ID of the GTM snippet (gtmScript in this guide).

Alt Text

Allowing custom HTML script

Now that the nonce variable is available in the GTM, add it to the Custom HTML script.

<script nonce="{{nonce}}">
  console.log("CSP-allowed script with nonce:", "{{nonce}}");
Enter fullscreen mode Exit fullscreen mode

If the tag is not firing, check the Support document.write. This can be a key step in Single Page Applications.
The GTM Custom HTML script is now nonce-allowed and fires as expected.
Of course, any assets used by this script will now need to be allowed in the CSP header.

Alt Text

Script within a script

Many tracking scripts are creating and firing additional script within themselves.
These will also be blocked as inline-scripts.
Find out where and how they are created, and add nonce to them as well.

Usually, the code looks similar to this:

var script = document.createElement("script");
script.type = "text/javascript";
script.async = true;
script.src = "https://tracking.js";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(script, s);
Enter fullscreen mode Exit fullscreen mode

Edit this part of the code and insert the nonce variable, in the same manner along with other attributes.

script.nonce = "{{nonce}}";
Enter fullscreen mode Exit fullscreen mode

Again, pay attention and whitelist any necessary assets that are now being blocked from this newly allowed script.

That's it - Custom HTML script is now fully CSP-allowed.

Discussion (1)

ranyehushua profile image

This was so helpful thank you! Googles own docs do not explain needing to add the variable in tag-manager console and adding to the scripts your self.

One problem in your solution is with Chrome. Chrome masks the nonce attribute value so tag manager is unable to grab it and store it as a variable. I solved this by adding a data-nonce attribute and injecting the nonce value there on that same gtmScript element. Works great!