CSP for Third Party Scripts: Google Analytics, Tag Manager, and Payment Providers
The exact domains you need to allowlist for every major third party service, organized by category
You built a Content Security Policy. It works great on your development server where your app only loads its own scripts. Then you deploy to production and everything breaks. Google Analytics stops collecting data. The Stripe checkout form disappears. The live chat widget vanishes. The Facebook Pixel stops firing.
The problem is that your CSP has no idea about the dozens of external services your production site depends on. Every third party script, every analytics pixel, every payment iframe, and every chat widget loads resources from external domains. If those domains are not in your policy, the browser blocks them silently.
This guide is the reference you keep open while building your CSP. It lists the exact domains you need to allowlist for every major third party service, organized by category. No guessing, no trial and error. Just the specific domains and directives each service requires.
How third party scripts interact with CSP#
A Content Security Policy is a single HTTP header your server sends with every page response. It contains a list of directives, and each directive controls which domains are allowed for a specific type of resource. Here is the basic format:
Content-Security-Policy: script-src 'self' cdn.example.com; connect-src 'self' api.example.com; img-src 'self' images.example.com; frame-src 'self' embed.example.com
Each directive name (script-src, connect-src, img-src, frame-src) tells the browser which resource type that rule applies to. 'self' means your own domain. After that, you list the external domains that are allowed. Directives are separated by semicolons, and domains within a directive are separated by spaces.
With that in mind, here are the four ways third party scripts can trigger CSP violations:
- Loading the script itself. The initial
<script src="...">tag loads from an external domain. If that domain is not listed in thescript-srcdirective of your CSP, the browser blocks it. - Making API calls. Once loaded, the script sends data back to its servers using XHR or fetch. The
connect-srcdirective in your CSP controls which domains the browser allows these network requests to reach. - Creating iframes. Payment forms and embedded widgets often run inside an iframe. The
frame-srcdirective in your CSP decides which domains are allowed to be embedded as iframes on your page. - Loading additional resources. The script may load images (tracking pixels), styles, or additional scripts from other domains. Each resource type is governed by its own CSP directive:
img-srcfor images,style-srcfor stylesheets, andscript-srcfor JavaScript files.
Use strict-dynamic for script chains
Many third party scripts load additional scripts dynamically after the initial script runs. Google Tag Manager is the prime example. Adding 'strict-dynamic' to your script-src directive tells the browser that scripts loaded by a trusted script are also trusted. This dramatically reduces the number of domains you need to explicitly list.
Analytics and tracking#
Google Analytics (GA4)
Google Analytics 4 is typically loaded through Google Tag Manager, but it can also be loaded directly. Here are the domains it needs:
| Directive | Domains |
|---|---|
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 |
Google Tag Manager
Google Tag Manager is the tricky one. GTM itself loads from a known domain, but the tags it fires can load scripts from anywhere. This is where 'strict-dynamic' becomes essential.
| Directive | Domains |
|---|---|
script-src | www.googletagmanager.com |
img-src | www.googletagmanager.com |
connect-src | www.googletagmanager.com |
GTM and inline scripts
GTM injects inline scripts for each tag it fires. Without nonces or 'strict-dynamic', these inline scripts will be blocked by your CSP. The recommended setup is: add a nonce to the initial GTM script tag and include 'strict-dynamic' in your script-src directive. This allows GTM to load all its tags without needing to list every domain individually. You will still need the domains listed above, plus the domains of any services GTM fires (GA, Ads, etc.) in the connect-src and img-src directives.
Facebook Pixel
| Directive | Domains |
|---|---|
script-src | connect.facebook.net |
connect-src | www.facebook.com |
img-src | www.facebook.com www.google-analytics.com |
frame-src | www.facebook.com |
Hotjar
| Directive | Domains |
|---|---|
script-src | static.hotjar.com script.hotjar.com |
connect-src | *.hotjar.com wss://*.hotjar.com |
frame-src | vars.hotjar.com |
img-src | static.hotjar.com script.hotjar.com |
font-src | static.hotjar.com |
Payment providers#
Payment providers are especially sensitive to CSP misconfigurations. If the checkout form fails to load, you lose revenue immediately. Always test payment flows thoroughly after deploying or updating your CSP.
Stripe
| Directive | Domains | Purpose |
|---|---|---|
script-src | js.stripe.com | Stripe.js library |
frame-src | js.stripe.com hooks.stripe.com | Payment Element, 3D Secure |
connect-src | api.stripe.com | API requests |
img-src | *.stripe.com | Card brand logos |
PayPal
| Directive | Domains | Purpose |
|---|---|---|
script-src | www.paypal.com www.paypalobjects.com | PayPal SDK |
frame-src | www.paypal.com www.sandbox.paypal.com | Payment buttons, checkout |
connect-src | www.paypal.com *.paypal.com | API and eligibility calls |
img-src | www.paypalobjects.com t.paypal.com | Logos, tracking pixel |
Square
| Directive | Domains |
|---|---|
script-src | js.squareup.com js.squareupsandbox.com |
frame-src | js.squareup.com js.squareupsandbox.com |
connect-src | connect.squareup.com connect.squareupsandbox.com pci-connect.squareup.com |
Customer support and engagement#
Intercom
| Directive | Domains |
|---|---|
script-src | widget.intercom.io js.intercomcdn.com |
connect-src | *.intercom.io wss://*.intercom.io uploads.intercomcdn.com |
frame-src | intercom-sheets.com |
img-src | static.intercomassets.com *.intercomcdn.com |
font-src | js.intercomcdn.com |
media-src | js.intercomcdn.com |
HubSpot
| Directive | Domains |
|---|---|
script-src | js.hs-scripts.com js.hsforms.net js.hs-analytics.net js.hs-banner.com js.hscollectedforms.net |
connect-src | api.hubspot.com forms.hubspot.com js.hs-analytics.net |
frame-src | app.hubspot.com |
img-src | track.hubspot.com *.hsforms.com |
Crisp Chat
| Directive | Domains |
|---|---|
script-src | client.crisp.chat |
connect-src | client.crisp.chat wss://client.relay.crisp.chat *.crisp.chat |
frame-src | game.crisp.chat |
style-src | client.crisp.chat |
img-src | client.crisp.chat image.crisp.chat storage.crisp.chat |
font-src | client.crisp.chat |
Common web services#
Google Fonts
| Directive | Domains |
|---|---|
style-src | fonts.googleapis.com |
font-src | fonts.gstatic.com |
Google reCAPTCHA (v2 and v3)
| Directive | Domains |
|---|---|
script-src | www.google.com www.gstatic.com |
frame-src | www.google.com |
YouTube Embeds
| Directive | Domains |
|---|---|
frame-src | www.youtube.com www.youtube-nocookie.com |
img-src | i.ytimg.com img.youtube.com |
Google Maps
| Directive | Domains |
|---|---|
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 |
Putting it all together: a real world example#
Here is what a CSP looks like for a typical SaaS marketing site that uses Google Tag Manager (with GA4), Stripe for payments, Intercom for support, Google Fonts, and reCAPTCHA on the contact form:
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-reportThis is a long policy, and that is normal. Real world CSPs with multiple third party services are not short. The important thing is that every domain is explicitly listed for the specific directives it needs. No wildcards on broad domains, no unsafe-inline in script-src, and strict-dynamic handles dynamic script loading.
Best practices for managing third party scripts in CSP#
Use the most specific domain possible
Always prefer js.stripe.com over *.stripe.com and definitely over https:. Each level of specificity narrows the attack surface. Only use wildcards when a service genuinely uses unpredictable subdomains.
Separate directives properly
Do not lump everything into default-src. If Stripe only needs js.stripe.com in script-src and frame-src, do not add it to default-src where it would also apply to images, styles, and everything else.
Audit regularly
Third party services change their domains. Google has migrated analytics endpoints multiple times. Stripe has added new domains for features like 3D Secure. Check your CSP violation reports regularly and update your policy when services change their infrastructure.
Document why each domain is there
Six months from now, someone on your team will look at your CSP and wonder why hooks.stripe.com is in frame-src. Add comments to your CSP config explaining which service each domain belongs to. This makes maintenance much easier.
Remove services you no longer use
When you drop a third party service, remove its domains from your CSP. Leftover allowlisted domains from services you no longer use are an unnecessary expansion of your attack surface. Every domain in your policy is a domain that could potentially serve malicious content if compromised.
How to find domains you are missing#
Even with this reference, your site might use services not listed here. Here is how to discover what domains your CSP needs:
- Browser DevTools Network tab. Open your site, go to the Network tab, and reload. Filter by domain to see every external domain your page contacts. Each one might need to be in your CSP.
- CSP violation reports. Deploy your policy in
Content-Security-Policy-Report-Onlymode with areport-uriendpoint. The browser will tell you exactly what is being blocked and which directive caused it. - Run a scan. SiteSecurityScore scans your site and shows your current CSP, what it covers, and what might be missing.
- Check the service's documentation. Many services now publish their CSP requirements. Stripe, Google, and others have documentation pages listing the exact domains their scripts use.
The bottom line#
Third party scripts are the most common reason CSP deployments fail. Not because the concept is hard, but because finding the right domains for each service is tedious and error prone. This guide gives you the starting point. Combine it with Content-Security-Policy-Report-Only mode to catch anything you missed, and you will have a production ready CSP that protects your users without breaking your integrations.
Remember that third party services change their infrastructure over time. Bookmark this page, but also keep your CSP reporting active. The combination of a good reference and real time violation data is what makes CSP manageable at scale.
Frequently asked questions#
How do I add Google Analytics to my Content Security Policy?
Add www.googletagmanager.com and www.google-analytics.com to script-src. Add www.google-analytics.com, analytics.google.com, and their wildcard variants to connect-src for data collection endpoints.
Why does Google Tag Manager need unsafe-inline in CSP?
GTM injects inline scripts for each tag it fires. The recommended solution is to use nonces with 'strict-dynamic' rather than 'unsafe-inline'. Add a nonce to the initial GTM script tag, and strict-dynamic will allow GTM to load its tags without listing every domain.
What CSP directives does Stripe require?
Stripe needs js.stripe.com in script-src, js.stripe.com and hooks.stripe.com in frame-src, and api.stripe.com in connect-src. For card brand logos, add *.stripe.com to img-src.
How do I handle third party scripts that load other scripts dynamically?
Add 'strict-dynamic' to your script-src. This tells the browser that scripts loaded by a trusted (nonced) script are also trusted. It is essential for tag managers, advertising platforms, and A/B testing tools that inject additional scripts at runtime.
Should I use wildcard domains in my CSP for third party services?
Avoid wildcards whenever possible. Using *.google.com instead of www.google-analytics.com opens your policy to any subdomain on that domain. Always use the most specific domain you can. Only use wildcards when a service genuinely uses unpredictable subdomains.
Continue reading
Is your CSP covering all your third party scripts?
Scan your website to check if your Content Security Policy accounts for every external service you use.