CSP and Google Analytics: A Guide to Not Blocking Yourself
Why your Google Analytics data might be zero despite the tag being installed — and how to fix Content Security Policy conflicts with GA4 and Google Ads.
You added the Google Analytics tag. You see it loading in the Network tab. But your dashboard shows zero users. Sound familiar?
The Problem
This was my browser console:
Refused to connect to 'https://analytics.google.com/g/collect'
because it violates the Content Security Policy directive:
"connect-src 'self' https://www.google-analytics.com https://www.google.com"
I had a CSP (Content Security Policy) header that allowed google-analytics.com, but GA4 was trying to send data to analytics.google.com — a different domain.
Why This Happens
Google Analytics 4 (the newer gtag.js) uses multiple endpoints for data collection:
| Endpoint | Used for |
|---|---|
www.google-analytics.com/g/collect |
Universal Analytics (UA) |
analytics.google.com/g/collect |
GA4 (the modern version) |
www.googletagmanager.com |
GTM script loading |
If your CSP only allows the first one, GA4 data silently fails.
The Fix
Add https://analytics.google.com to your connect-src directive:
Content-Security-Policy:
default-src 'self';
connect-src 'self'
https://www.google-analytics.com
https://www.google.com
https://analytics.google.com; ← This one
...
Bonus: Google Ads Image Tracking
If you also use Google Ads, you might see another error:
Refused to load image from 'https://www.google.com.tw/ads/ga-audiences'
because of img-src restriction
This is Google's audience tracking. The image request can come from any localized Google domain (.com.tw, .co.jp, .de, etc.). The fix is to add https://*.google.com to your img-src:
img-src 'self' blob: data: https://*.google.com;
Full Example (Next.js)
In next.config.mjs:
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Content-Security-Policy',
value: [
"default-src 'self'",
"script-src 'self' 'unsafe-inline' https://www.googletagmanager.com",
"style-src 'self' 'unsafe-inline'",
"img-src 'self' blob: data: https://*.google.com",
"font-src 'self'",
"connect-src 'self' https://www.google-analytics.com https://www.google.com https://analytics.google.com",
"object-src 'none'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'none'",
].join('; '),
},
],
},
];
},
};
How to Debug CSP Issues
- Open Chrome DevTools → Console. CSP violations show as clear error messages with the violated directive
- Each error tells you exactly which URL was blocked and which directive caused it
- Temporarily remove the CSP header in dev mode to confirm the data flow works
- Use the
report-uriorreport-todirective to collect violations programmatically
Does CSP Affect Googlebot?
No. Googlebot does not execute JavaScript in a way that is subject to CSP. The raw HTML content is indexed fine. But CSP does affect GA4 data collection from real users, which means you get zero analytics data.
Summary
| Directive | Why you need it |
|---|---|
connect-src + analytics.google.com |
GA4 data collection |
img-src + *.google.com |
Google Ads audience tracking |
script-src + googletagmanager.com |
Loading gtag.js |
If your GA4 dashboard shows zero data but the tag is installed, check your CSP. It took me an afternoon to find this — hopefully this saves you the same.