You ship a Content Security Policy. It works locally. Then marketing adds Google Tag Manager, payments goes live with Stripe, support turns on Intercom, and suddenly the browser console is screaming about violations and half your integrations are dead.
This is the number one reason CSP deployments fail in production. Not because the concept is hard, but because every third party service loads scripts from a different set of domains, and none of them make it easy to find.
This post is a working cheat sheet for the most common services. The full version with every table, nonce setup, and a strict-dynamic deep dive lives on the main guide: CSP for Third Party Scripts on SiteSecurityScore.
Why third party scripts break CSP
Every script, image, iframe, font, or XHR call your page makes has to match one of your CSP directives. Third party services typically trigger violations in four ways.
- They load a script from their CDN, which needs to be in
script-src. - That script fetches data from an API, which needs
connect-src. - It loads an iframe for a widget or checkout, which needs
frame-src. - It pulls images, fonts, or styles from their domain, which need
img-src,font-src, andstyle-src.
Most services hit three of these four. That is why a CSP that looked clean on day one falls apart the moment someone wires up a tag manager.
Google Analytics 4 with GTM
script-src www.googletagmanager.com www.google-analytics.com
connect-src www.google-analytics.com analytics.google.com
*.google-analytics.com *.analytics.google.com
img-src www.google-analytics.com www.googletagmanager.com
GTM injects inline scripts for every tag it fires. Do not reach for 'unsafe-inline'. Use a nonce on the initial GTM script and add 'strict-dynamic' to script-src, and GTM can load its tags without you allowlisting every third party it chains to.
Facebook Pixel
script-src connect.facebook.net
connect-src www.facebook.com
img-src www.facebook.com
Hotjar
script-src static.hotjar.com script.hotjar.com
connect-src *.hotjar.com *.hotjar.io wss://*.hotjar.com
img-src *.hotjar.com
font-src *.hotjar.com
frame-src vars.hotjar.com
Stripe
script-src js.stripe.com
frame-src js.stripe.com hooks.stripe.com
connect-src api.stripe.com
img-src *.stripe.com
Stripe is strict. If you miss hooks.stripe.com in frame-src, 3D Secure challenges will silently fail and users will see a blank modal.
PayPal
script-src www.paypal.com www.paypalobjects.com
frame-src www.paypal.com www.sandbox.paypal.com
img-src www.paypalobjects.com
connect-src www.paypal.com
Intercom
script-src widget.intercom.io js.intercomcdn.com
connect-src *.intercom.io wss://*.intercom.io api-iam.intercom.io
img-src static.intercomassets.com *.intercomcdn.com
frame-src intercom-sheets.com
font-src js.intercomcdn.com
media-src js.intercomcdn.com
Watch the WebSocket entry on connect-src. If you forget wss://*.intercom.io, the widget loads but never connects, and support thinks the chat is broken.
HubSpot
script-src js.hs-scripts.com js.hs-analytics.net js.hsadspixel.net
js.hsforms.net js.hs-banner.com
connect-src api.hubspot.com forms.hubspot.com
img-src track.hubspot.com forms.hubspot.com
frame-src app.hubspot.com
Crisp Chat
script-src client.crisp.chat
connect-src client.crisp.chat wss://client.relay.crisp.chat
img-src client.crisp.chat image.crisp.chat
style-src client.crisp.chat
font-src client.crisp.chat
Google Fonts
style-src fonts.googleapis.com
font-src fonts.gstatic.com
reCAPTCHA v3
script-src www.google.com www.gstatic.com
frame-src www.google.com
YouTube embeds
frame-src www.youtube.com www.youtube-nocookie.com
img-src i.ytimg.com img.youtube.com
Google Maps
script-src maps.googleapis.com
frame-src www.google.com maps.google.com
img-src maps.googleapis.com maps.gstatic.com *.ggpht.com
connect-src maps.googleapis.com
A realistic SaaS CSP
Here is what a production CSP looks like when you stitch GTM, Stripe, Intercom, Google Fonts, and reCAPTCHA together.
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-{value}' 'strict-dynamic'
www.googletagmanager.com
www.google.com www.gstatic.com
js.stripe.com
widget.intercom.io js.intercomcdn.com;
style-src 'self' 'nonce-{value}'
fonts.googleapis.com;
font-src 'self'
fonts.gstatic.com
js.intercomcdn.com;
img-src 'self' data: https:
www.googletagmanager.com
www.google-analytics.com
static.intercomassets.com *.intercomcdn.com
*.stripe.com;
connect-src 'self'
www.google-analytics.com analytics.google.com
*.google-analytics.com *.analytics.google.com
api.stripe.com
*.intercom.io wss://*.intercom.io;
frame-src 'self'
www.google.com
js.stripe.com hooks.stripe.com
intercom-sheets.com;
media-src 'self'
js.intercomcdn.com;
report-uri /api/csp-report
Long, but every line has a reason. No wildcards on broad domains, no 'unsafe-inline' in script-src, and strict-dynamic handles anything GTM or Intercom chain-loads at runtime.
A few rules that keep this sane
Prefer the most specific domain. js.stripe.com beats *.stripe.com, which beats https:. Every level of specificity is one less place an attacker can hide.
Do not dump everything into default-src. If Stripe only needs script-src and frame-src, leaving it in default-src silently allows it for images and fonts too.
Audit on a schedule. Google has migrated analytics endpoints multiple times. Stripe has added domains for new checkout flows. Your CSP from last year is probably already wrong.
Delete services you no longer use. Every allowlisted domain you forgot about is a domain an attacker can abuse if that vendor ever gets compromised.
How to find the domains you missed
Even with a cheat sheet, your site uses something this post does not list. Three ways to find out what.
Ship in report-only mode first. Set Content-Security-Policy-Report-Only with a report-uri or report-to endpoint. The browser sends you a structured violation for every blocked request without breaking production. Walk through the funnel, collect the reports, then tighten the policy. The full walkthrough is in How CSP Reporting Works.
Use DevTools Network tab. Open the site, reload with the Network tab open, sort by domain. Every external host your page touches is a candidate for your CSP.
Scan the site. SiteSecurityScore parses your live CSP, shows which directives are set, and flags gaps for the services it detects on your page.
Free tools that helped me write this
These are all on the main site and free to use, no account needed.
-
CSP Generator builds a policy from a checklist of services and gives you a working
Content-Security-Policyheader. -
CSP Reporting Guide shows how to stand up a
report-uriendpoint and interpret the violation payloads. - HSTS Generator for the other header everyone gets wrong.
- Permissions Policy Generator for locking down camera, mic, geolocation, and payment APIs.
- CORS Generator for the adjacent problem of cross origin requests.
- The main security scanner grades headers, TLS, cookies, CSP coverage, and third party script exposure in one pass.
TL;DR
Third party scripts break CSP because nobody documents the exact domains their service needs across every directive. Steal the tables above, start in report-only mode, watch your violation reports, and iterate until production is quiet.
Full version with nonce setup, strict-dynamic explanation, and a complete FAQ is on the main guide: CSP for Third Party Scripts on SiteSecurityScore.
If your site is already in production and you want a quick read on whether your CSP actually covers the scripts you are loading, run it through the scanner. Takes about 10 seconds.
Top comments (0)