Free interactive tool
CSP Checker for Tracking Tags
Paste your Content-Security-Policy and a tracking tag, and see which of the tag’s requests the policy blocks, which directive blocks it, and the exact source to add. A tag needs up to four separate permissions: the inline install snippet, the script it loads, the beacon requests that send each visit, and sometimes an image pixel or frame. The checker applies the matching rules from the W3C Content Security Policy Level 3 specification to each one. Security graders such as Google’s CSP Evaluator rate how well a policy stops cross-site scripting; this answers a different question: will this specific tag run under this specific policy? Nothing you paste leaves your browser.
The rules it applies
for each request the tag makes, in each enforced policy:
directive = first present of the fallback chain
script : script-src-elem → script-src → default-src
beacon : connect-src → default-src
pixel : img-src → default-src
frame : frame-src → child-src → default-src
no directive in the chain → not restricted
inline snippet → 'unsafe-inline' (unless a nonce, hash or
'strict-dynamic' cancels it), a matching
nonce, or a matching hash
'strict-dynamic' in directive → host lists and 'self' ignored for scripts;
scripts created by an allowed script load,
a <script src> in the HTML needs the nonce
otherwise → URL must match a source: scheme, host
(*.x.com = any subdomain, not x.com),
port, path
request runs only if every enforced policy allows it;
report-only policies report and never blockEach line follows a named algorithm in the specification: the effective directive (§ 6.8.1), the fallback list (§ 6.8.3), the script pre-request check (§ 6.7.1.1), URL matching (§ 6.7.2.7 to 6.7.2.12), the inline check (§ 6.7.3.2) and multiple policies (§ 8.1). The checker tests the URL you enter, not where it redirects. If a tag host redirects to another host, that host must be allowed too (after a redirect the browser checks the host but not the path), so add the final URL from the Network tab.
Check your policy
The page the tag runs on. ‘self’ and scheme-less hosts resolve against it.
Paste the header value from DevTools → Network → the page document → Response Headers. Add the “Content-Security-Policy-Report-Only:” prefix for a report-only policy, or paste a <meta http-equiv> tag. If the page sends both a header and a meta policy, paste both lines: each one must allow the request.
Presets fill the fields below. Edit any field to match your install.
Inline code is checked as an inline script (and hashed, so a matching ‘sha256-…’ source is recognised). A <script src> tag is checked as a parser-inserted script. If the tag is fired from a tag manager, paste the container snippet here; left empty, the checker assumes the loader is allowed.
One URL per line. Checked against script-src-elem → script-src → default-src.
One URL per line. Checked against connect-src → default-src.
One URL per line. Checked against img-src → default-src.
One URL per line. Checked against frame-src → child-src → default-src.
| Request | Result | Why, and the fix |
|---|---|---|
Inline snippetinline code | allowed | script-src has 'unsafe-inline' and no nonce, hash or 'strict-dynamic' to cancel it. |
Scripthttps://cdn.vendor.example/tag.js | allowed | cdn.vendor.example matches https://cdn.vendor.example in script-src. |
Beacon / fetchhttps://collect.vendor.example/v1/events | blocked | collect.vendor.example matches nothing in default-src ('self'). Fix: Add https://collect.vendor.example to default-src, or add a connect-src directive — but a new connect-src replaces default-src for these requests entirely, so copy default-src's sources ('self') into it as well. |
CSP tag check: visitorops.com/tools/csp-tag-checker
Page origin: https://www.example.com
Policy 1 (enforce): default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.vendor.example; img-src 'self' data:
ALLOWED Inline snippet: inline code
policy 1: script-src has 'unsafe-inline' and no nonce, hash or 'strict-dynamic' to cancel it.
ALLOWED Script: https://cdn.vendor.example/tag.js
policy 1: cdn.vendor.example matches https://cdn.vendor.example in script-src.
BLOCKED Beacon / fetch: https://collect.vendor.example/v1/events
policy 1: collect.vendor.example matches nothing in default-src ('self'). Fix: Add https://collect.vendor.example to default-src, or add a connect-src directive — but a new connect-src replaces default-src for these requests entirely, so copy default-src's sources ('self') into it as well.Runs entirely in your browser: nothing you paste is sent anywhere. “Allowed” means this policy lets the request through. A consent banner, an ad blocker or a tag-manager trigger can still stop the tag.
Worked example (hypothetical vendor)
A site sends default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.vendor.example and installs a visitor-identification tag whose snippet loads https://cdn.vendor.example/tag.js, which then posts each visit to https://collect.vendor.example (a hypothetical vendor; this is the checker’s default input). The snippet passes on 'unsafe-inline' and the script passes on its host. The beacon fails: the policy has no connect-src, so default-src 'self' decides, and the vendor’s host is not the page’s own. In DevTools the script shows 200 in the Network tab, the console shows a connect-src violation, and the vendor dashboard receives nothing. The tag looks installed and records no visits.
The fix is connect-src 'self' https://collect.vendor.example. The 'self' matters: a new connect-src replaces default-src for every fetch, XHR and beacon, so leaving it out would break the site’s own API calls.
Now the security team moves the site to script-src 'nonce-…' 'strict-dynamic'. The host list stops mattering for scripts. The inline snippet needs the per-response nonce; once it has it, the tag.js it creates with createElement loads with no host entry. A tag installed as a plain <script src> in the HTML, which is how HubSpot’s tracking code is installed, is blocked until the server writes the nonce into that tag too. (On HubSpot-hosted pages with its “Enable nonce” setting on, HubSpot adds the nonce to its own scripts.) Choose the HubSpot preset above and add script-src 'nonce-abc' 'strict-dynamic' to the policy to see it.
Where do the tag’s hosts come from?
The snippet shows only the first script. The hosts that matter most, the ones the beacons go to, appear only when the tag runs. Three ways to get the full list, most reliable first:
- Run the tag on a page with no policy (a staging page, or the vendor’s own test page if it offers one) and filter DevTools → Network by the vendor’s domain. Copy every host that appears under JS, Fetch/XHR, Img and Doc. List them by type in the fields above.
- Read the browser’s own verdict. On the page with the policy, the console prints one line per blocked request naming the URL and the directive. To catch beacons that fire later, paste this into the console, then scroll and click through the page:
document.addEventListener('securitypolicyviolation', e => console.log(e.effectiveDirective, e.blockedURI)). - Use the vendor’s documented list where one exists. HubSpot publishes a table of domains and directives on its domain security page (updated 17 September 2026; the tracking code is
*.hs-scripts.comin script-src), and Google documents the Tag Manager requirements in Use a Content Security Policy (updated 18 September 2026). Some vendors do not publish one: RB2B’s CSP troubleshooting article (30 June 2026) tells customers to ask its in-dashboard support chat for the exceptions. Treat any list as a starting point and confirm it with step 1: vendors add hosts without notice.
The two presets use only what those pages state. HubSpot says to expect the loader from js.hs-scripts.com, the tracking script from js.hs-analytics.net and a js.hs-banner.com script, per its tracking-code troubleshooting page. The Tag Manager preset covers the container only. Every tag the container fires brings its own hosts, and Google notes that Custom JavaScript variables need 'unsafe-eval'.
What does “allowed” not tell you?
- Consent. A consent platform can hold the tag before the policy is ever consulted. See how OneTrust, Cookiebot and CookieYes hold a script until consent.
- Ad and tracker blockers. Browser extensions block by domain list, independent of your policy. A clean result here does not predict how many visitors run the tag.
- Nonce values. The checker takes your word that the snippet carries the right nonce. In production the nonce must be new on every response and identical in the header and the tag. If a cache serves stored HTML with a freshly generated header, the two stop matching and every nonce-gated script is blocked.
- Single-page route changes. A tag that loads once may not record later client-side page views. That is a trigger problem, covered in the JavaScript framework install guide.
- Directives it does not check. style-src, font-src and worker-src;
'unsafe-eval'(Tag Manager Custom JavaScript variables need it); inline event handlers such asonload=(script-src-attr); and scripts written withdocument.write, which count as parser-inserted and are blocked under'strict-dynamic'. An older browser that does not know script-src-elem falls back to script-src. - Other headers. Cross-origin rules (CORS), Permissions-Policy and an iframe’s own
frame-ancestorscan each stop a request this checker passes.