Your CSP probably breaks Google Analytics, and only sometimes
- Security
- Analytics
- CSP
We added a Content Security Policy to this site at the same time as Google Analytics. The policy is hash based, with a sha256 for every inline script, because a company that sells penetration testing should not ship the one script-src value that makes the whole policy decorative.
It passed on the home page. It passed on the services pages. It failed on two.
What actually happens
Everyone knows gtag.js is a script from googletagmanager.com, so script-src gets that origin and the job looks done. What is less obvious is that gtag also fires image requests at the same origin, to paths like /a and /td, and it does not fire them on every page load. It samples.
When we loaded all 29 routes under the live policy, those image requests appeared on exactly two. Had we tested the home page and shipped, the policy would have been silently wrong, and the symptom would have been analytics data that was quietly incomplete rather than obviously broken.
The fix
img-src needs the tag manager origin, not just script-src. The full set we observed, with Google advertising features enabled on the property:
script-src https://www.googletagmanager.com
img-src https://www.googletagmanager.com
https://www.google-analytics.com
https://stats.g.doubleclick.net
https://www.google.com
https://www.google.co.za
connect-src https://www.google-analytics.com
https://analytics.google.com
https://region1.google-analytics.com
https://stats.g.doubleclick.netThe doubleclick and google ccTLD entries are only needed while advertising features are switched on in the GA property. Turn Google signals off and those two disappear, which is a good reason to check whether you need them at all.
The part worth generalising
Test every route, not a representative one. Third-party tags sample their own telemetry, so a policy that passes on one page is not a policy that passes.
We now load every route in headless Chrome on each build and fail on any CSP violation, console error or hydration failure. It takes about two minutes and it has caught two things that would otherwise have reached production.
One more detail that catches people: a strict CSP also applies to JSON-LD. Browsers do not execute a script of type application/ld+json, but the policy still evaluates the element, so your structured data needs a hash too. Miss it and you lose your rich results without losing anything visible.
More from us
Want this standard applied to your systems?
We write these up because they are the same decisions we make on client work. Tell us what you are building and we will tell you what we would do.
A reply from a developer. Not a sales pipeline. The person who reads your message is the person who would do the work.
An honest answer on fit. If an off-the-shelf product solves your problem better than a custom build, we will tell you which one.
No obligation. A first conversation costs nothing and commits you to nothing. Discovery output is yours either way.