The Symptom
GTM Preview mode fails to connect. Tags are not firing. GA4 shows no data. Your GTM container is published and the snippet is on the page - but nothing works.
No obvious errors in GA4. No warnings in GTM. Just silence.
This is Content Security Policy (CSP) blocking your tags, and it is one of the least visible causes of complete tracking failure.
What Is Content Security Policy?
Content Security Policy is an HTTP response header (or <meta> tag) that instructs the browser which external resources are allowed to load on a page.
A strict CSP header looks like this:
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-abc123';
connect-src 'self' https://api.example.com;
img-src 'self' data:;
This tells the browser: only load scripts that come from the same origin OR that have the correct nonce value. Any script without a nonce - including GTM’s container script - is blocked.
The browser does not show a user-facing error. It silently refuses to execute the script. From GA4’s perspective, nothing happened.
Where CSP Is Common
- Next.js / Vercel deployments - CSP is often configured in
next.config.jsor via Vercel headers - Shopify Headless / Hydrogen - strict policies enabled by default
- Enterprise CMS platforms (Contentful, Sanity with custom frontends)
- Security-hardened WordPress setups with security plugins like Wordfence or NinjaFirewall
- Any site built by a security-conscious development team
If you are working with a developer-built site and GTM suddenly stopped working after a deployment, CSP is the first thing to check.
How to Diagnose a CSP Block
Step 1: Check the Browser Console
Open Chrome DevTools → Console tab. Look for errors that look like:
Refused to load the script 'https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX'
because it violates the following Content Security Policy directive:
"script-src 'self' 'nonce-abc123'"
If you see this, CSP is your problem.
Step 2: Check the Response Headers
DevTools → Network tab → reload the page → click the HTML document request → Headers tab → look for Content-Security-Policy in the response headers.
Read through the policy. Look specifically at:
script-src- controls which scripts can executeconnect-src- controls which URLs JavaScript can send requests toimg-src- relevant for tracking pixels
Step 3: Check GTM Preview Mode Failure
If GTM Preview mode shows “Waiting for the Tag Assistant to connect” and never connects, CSP is a very likely cause - Preview mode loads scripts from tagassistant.google.com which needs to be whitelisted.
The Fix: Whitelist Google Tag Manager Domains
The simplest fix is to add the required Google domains to your CSP policy.
Required Domains for GTM
script-src (scripts that need to execute):
https://www.googletagmanager.com
connect-src (URLs that tags send data to):
https://www.google-analytics.com
https://analytics.google.com
https://stats.g.doubleclick.net
https://www.googleadservices.com
https://googleads.g.doubleclick.net
https://www.google.com
img-src (for pixel-based tracking):
https://www.google-analytics.com
https://www.googletagmanager.com
https://googleads.g.doubleclick.net
https://www.google.com
frame-src (for GTM Preview mode):
https://tagassistant.google.com
https://www.googletagmanager.com
A complete policy addition for GTM looks like:
Content-Security-Policy:
script-src 'self' https://www.googletagmanager.com [your-other-sources];
connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://stats.g.doubleclick.net https://www.googleadservices.com https://googleads.g.doubleclick.net [your-other-sources];
img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com https://googleads.g.doubleclick.net [your-other-sources];
frame-src https://tagassistant.google.com https://www.googletagmanager.com [your-other-sources];
The Nonce Approach (For Strict Policies)
Some sites use strict-dynamic with nonces - this means no domain whitelisting is allowed. Only scripts with a specific cryptographic nonce value are permitted to execute.
What Is a Nonce?
A nonce is a random value generated per request by the server and embedded in:
- The CSP header:
script-src 'nonce-abc123' 'strict-dynamic' - Any script tag allowed to run:
<script nonce="abc123" src="...">
Scripts without a matching nonce are blocked.
How to Apply a Nonce to GTM
The GTM snippet needs the nonce attribute. Ask your developer to inject the server-generated nonce into the GTM script tag:
<script nonce="SERVER_GENERATED_NONCE" async
src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX"></script>
GTM also needs to pass this nonce to any scripts it injects. This is handled by setting the nonce on the GTM container config. In your inline GTM init script:
<script nonce="SERVER_GENERATED_NONCE">
(function(w,d,s,l,i){
w[l]=w[l]||[];
w[l].push({'gtm.start': new Date().getTime(), event:'gtm.js'});
var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),
dl=l!='dataLayer'?'&l='+l:'';
j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
j.setAttribute('nonce','SERVER_GENERATED_NONCE');
f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXX');
</script>
GTM will propagate the nonce to scripts it injects when strict-dynamic is in use.
Tags Loaded by GTM Also Need CSP Coverage
Here is where it gets complicated: every tag GTM loads is also subject to CSP.
If you add a Meta Pixel tag, a LinkedIn tag, or any custom HTML tag inside GTM - those scripts need their domains whitelisted too.
This means your CSP must cover not just GTM itself but every third-party tool GTM loads:
- Meta Pixel:
connect.facebook.net,www.facebook.com - LinkedIn:
snap.licdn.com,px.ads.linkedin.com - Hotjar:
static.hotjar.com,vars.hotjar.com - Intercom:
widget.intercom.io,js.intercom.io
Any tag you add to GTM in future may silently fail if its domain is not in the CSP policy. This is an ongoing maintenance challenge - every new tool added to GTM may require a corresponding CSP update.
Communicating This to Developers
Developers who set up CSP often do not know which external services marketing tools need. The conversation usually goes like this:
“We have a strict CSP for security reasons. What domains does your tool need?”
Come prepared with a list of every domain your current tags require. Frame it as a security discussion, not a marketing request - developers respond better when you acknowledge the security intent and work within it.
A practical approach: maintain a shared document with “domains required per tool” so every new GTM tag addition is matched with a CSP update request.
Testing After Making CSP Changes
- Have the developer deploy the CSP update to a staging environment
- Open the browser console and confirm no CSP errors appear
- Test GTM Preview mode - it should connect successfully
- Navigate through the site and confirm tags fire in Tag Assistant
- Check GA4 DebugView for incoming events
- Deploy to production and monitor for 24 hours
Never assume CSP changes are correct without testing. A policy that is too loose reintroduces security risk. A policy that is still too strict leaves tags blocked.
Final Thoughts
CSP problems are invisible until you know to look for them. The symptom - tags not firing, GTM Preview failing - looks exactly like a dozen other problems. The console error is the giveaway.
If you work on developer-built sites, make CSP your first diagnostic check when GTM mysteriously stops working after a deployment.
In the next article of this series, we will cover:
Cross-domain tracking breaking at iframes and third-party checkouts - and how to fix it.
Related Posts
Duplicate Conversions in GTM - Why Your Purchase Event Fires Twice and How to Diagnose It
9 min read
How to Block China Traffic in Google Tag Manager
WooCommerce Google Ads Conversion Tracking via GTM Using GTM4WP
14 min read
Need Help With Your Google Ads?
I help e-commerce brands scale profitably with data-driven PPC strategies.
Get In Touch