Using Google Tag Manager with a Content Security Policy

Using GTM with a CSP? Not exactly straightforward. Why that is, how it still works and how to avoid the biggest pitfalls is what you’ll learn here.

Published: 09.06.2025 | Updated: 22.02.2026
Content: Content Security Policy (CSP), Google Tag Manager (GTM), nonce variable, custom JS variable

You probably found this article because your tracking suddenly stopped working – and you spotted a cryptic error message in the console telling you that the Content Security Policy (CSP) prevented the execution of your tracking:

Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' https://trusted.cdn.com". Either the 'unsafe-inline' keyword, a hash ('sha256-abc123...'), or a nonce ('nonce-...') is required to enable inline execution.

Clear step-by-step instructions on how to solve this exact issue with Google Tag Manager are surprisingly rare – or you end up with developer-focused tutorials that don’t help much in your situation. Been there, done that. That’s exactly why I’m showing you here how to identify and fix the issue step by step – no coding ability needed.

How to find the CSP problem blocking your Tag Manager

The error message you’ve likely already seen – if not, press F12 or Ctrl + Shift + C – already gives you the first clue about which script is causing trouble. On the left-hand side, you should see which file violated the CSP.

CSP errors from my site with highlighting

The console tells you which file and which line is causing issues.

You can also check the “Network” tab to see which requests were blocked by the CSP. In the status column, the blocked requests show as (blocked:csp) along with additional information you should note down (domain, type). This helps developers configure the CSP correctly for your site. I explain more about this specifically in the article on subresource loading and third-party scripts.

It is especially helpful to check in the Network tab whether the file gtm.js?id=GTM-[XXXXXXXX] was blocked. That’s your Google Tag Manager container. If it wasn’t blocked, you should verify whether the inline script loaded:

Info: A nonce («number used once») is a one-time token generated each time the page loads – essentially a single-use password for the script’s execution. A hash, in turn, is a kind of checksum or fingerprint for your script. It only matches if the script does not change and if the script itself does not load additional resources (e.g. externally hosted JS files).


Spoiler Alert: GTM works only with unsafe-inline or a nonce

First things first: for very simple, fully static websites without logins or forms, a strict CSP can be overkill. In such cases, the risk of an inline vulnerability is lower – and some site owners choose to allow unsafe-inline because the level of security is sufficient for their scenario. My own website falls into this category. If your site is similar, it may be worth discussing with your IT department whether the CSP is necessary and whether the downstream effects on your tracking setup have been considered in the risk assessment. If yes, you’ll unfortunately still have a bit of extra work ahead.

As mentioned earlier, you usually cannot use Google Tag Manager together with a CSP unless you generate a nonce. At least not without leaving the 'unsafe-inline' security hole open and substantially weakening the CSP’s protection. And if you’re thinking about simply loading the script externally in a file, I have bad news: the GTM snippet relies on an inline implementation, for example to correctly initialize the data layer.

For this reason alone, you won’t get around asking your IT team whether they can "generate a nonce for each pageload and push it into the DOM" and whether they can add the nonce to the GTM snippet in the head section:

Head of my site with nonces

Alternative: You can migrate your GTM setup entirely to server-side tracking. Since a Content Security Policy restricts what the browser is allowed to load, server-side tagging bypasses this issue because the tracking scripts don’t load in the browser but on the server your website is served from.


How to use your nonce in GTM

Once your GTM snippet has received the nonce, you can reuse it. However, the CSP will still block all external integrations (e.g. Hotjar) and custom HTML tags. At least the latter can be fixed using the same method as for the GTM snippet. But since the nonce is regenerated with every page load, you need to capture this value dynamically and insert it into your tags. The easiest approach is using a DOM element variable with a CSS selector. The exact selector depends on your site’s structure, but it could look something like this:

A nonce variable in GTM

Once the variable is created, enable preview mode in Tag Assistant and test whether the value is captured correctly. If that works, you just need to update your custom HTML tags. Change <script> to <script nonce="{{nonce}}">. It is important that you actually type {{nonce}} and do not paste it: GTM only accepts dynamic values from variables if they were selected via the dropdown menu. You may also need to check the box for document.write before your custom HTML tags work again.

Using a nonce variable in GTM custom HTML

You should see the context menu as soon as you start typing {{

Note: This doesn’t apply only to nonces. If you’re having trouble using variables inside a custom HTML script, check whether the variable was selected from the context menu that appears when typing. If you copy and paste it, Tag Manager will not recognise it as a variable and will interpret it as plain text.


Custom JavaScript variables & CSP

You may have noticed that I skipped one method of using your own JavaScript within Google Tag Manager: custom JavaScript variables. If your tracking setup relies on custom JS variables, I have bad news: You will almost certainly not be able to use them with a CSP. At least not without opening a security hole in your IT setup. A custom JS variable requires that 'unsafe-eval' is allowed in the CSP. And that is even riskier than allowing 'unsafe-inline'.

Think of it like this: allowing unsafe-inline is like leaving the keys inside your unlocked car. Allowing unsafe-eval is like leaving your car registration and a signed blank transfer form on the passenger seat so the thief can legally transfer ownership. The security difference between these two rules is enormous, and unsafe-eval usually allows attackers to dig much deeper into your system and makes recovery extremely difficult.


Is your tracking working again?

If the main issue was that your Google Tag Manager container stopped loading after implementing the CSP, or that your custom HTML scripts stopped working, this should have solved your problem. If some parts of your tracking now work again but social media pixels or other tools embedded as part of a custom HTML tag or template are still causing trouble, there is an additional step you need to consider. I'll explain what exactly here.

Alternatively, feel free to reach out and we can work together to solve your issue.