Content Security Policy Blocking Pixels: How to Fix It

Quick fix: Open the browser console and find the CSP error. It names the blocked URL and the directive that blocked it. Add that host to the directive named in the error (usually script-src, connect-src or img-src), allow the inline base code with a nonce, and test the new policy in report-only mode before enforcing it.
A Content Security Policy (CSP) tells the browser which sources a page may load scripts, images, frames and network requests from. It is a strong defense against cross-site scripting. It is also very good at silently breaking tracking: the pixel works on staging, someone tightens the policy in production, and conversions stop.
The good news is that CSP failures are explicit. The browser logs exactly what it blocked and why. This guide shows how to read that error, where the policy comes from, which directives and hosts common pixels need, and how to test changes safely.
Step 1: Read the console error
Open DevTools, go to the Console tab and reload the page. A CSP block in Chrome looks like this:
Refused to load the script 'https://connect.facebook.net/en_US/fbevents.js'
because it violates the following Content Security Policy directive:
"script-src 'self'". Note that 'script-src-elem' was not explicitly set,
so 'script-src' is used as a fallback.
The exact wording varies by browser and version, but every CSP error gives you three things:
- The resource type: a script, an image, a frame or a connection (a
fetch, XHR or beacon request). - The blocked URL: the host you need to allow, or decide not to allow.
- The directive that blocked it, including any fallback. If the error mentions
default-src, there is no specific directive for that resource type, and the default applies.
Also check the Network tab. Chrome marks CSP-blocked requests with a status of (blocked:csp), which helps when the console is noisy.
A second common error concerns the base code itself:
Refused to execute inline script because it violates the following
Content Security Policy directive: "script-src 'self'". Either the
'unsafe-inline' keyword, a hash ('sha256-...'), or a nonce ('nonce-...')
is required to enable inline execution.
Most pixel base codes are inline <script> blocks that then load a library from the vendor. A strict policy blocks the inline block first, so the library is never even requested. In that case you will not see a blocked vendor URL at all, only the inline script error.
Step 2: Find where the policy is set
You need to edit the policy at its source, so find out where it comes from.
- In the Network tab, click the main document request and look under Response Headers for
Content-Security-Policy. - View the page source and search for
http-equiv="Content-Security-Policy"in a<meta>tag. - If you find both, or several headers, every policy applies. A resource must pass all of them, so loosening one policy does nothing while another still blocks the host.
Policies are commonly set by a CDN or edge rule, the hosting platform, a security plugin, server configuration, or framework middleware. If you cannot find it in the codebase, check those layers.
Step 3: Match the resource to the right directive
| Directive | What it controls for pixels |
|---|---|
script-src / script-src-elem | Loading the vendor library and running the inline base code. script-src-elem covers <script> elements and falls back to script-src, then default-src. |
connect-src | Event requests sent with fetch(), XMLHttpRequest or navigator.sendBeacon(). Many modern pixels send events this way. |
img-src | Image beacons, including <noscript> fallback pixels and tags that send hits as image requests. |
frame-src | Iframes that some tags create, such as certain Google tags. |
default-src | Fallback for any of the above that is not set explicitly. |
A frequent mistake is allowing a host in script-src only. The library loads, but its event requests go out through connect-src or img-src and are blocked there. The pixel object exists, your code calls it without errors, and nothing arrives at the platform.
Step 4: Allow the hosts each pixel needs
The table below lists hosts from official base code and documentation as a starting point. Vendors change endpoints, and features like enhanced matching or regional collection can add more. The blocked URL in your console is always the final authority.
| Platform | Typical hosts and directives |
|---|---|
| Meta Pixel | script-src: https://connect.facebook.net (the base code loads fbevents.js from here). img-src and connect-src: https://www.facebook.com (the /tr endpoint). |
| Google Tag Manager | script-src-elem: a nonce for the container snippet. For preview mode, add https://www.googletagmanager.com and https://tagmanager.google.com plus the style, image and font hosts Google lists. |
| Google Analytics 4 | script-src-elem: https://www.googletagmanager.com. connect-src: https://*.google-analytics.com, https://www.googletagmanager.com, https://*.google.com. img-src: https://*.google-analytics.com, https://www.googletagmanager.com. With Ads features enabled, more Google and DoubleClick hosts and frame-src https://www.googletagmanager.com. |
| Google Ads conversions and remarketing | script-src-elem: https://www.googletagmanager.com, https://www.googleadservices.com, https://www.google.com. connect-src and img-src: those hosts plus https://googleads.g.doubleclick.net, https://pagead2.googlesyndication.com and country Google domains. frame-src: https://www.googletagmanager.com. |
| TikTok Pixel | https://analytics.tiktok.com, which serves the pixel library and receives events. Allow it in script-src and connect-src, and in img-src if your console reports blocked image requests. |
| LinkedIn Insight Tag | LinkedIn lists snap.licdn.com (script) and px.ads.linkedin.com among the domains the tag needs, along with several other LinkedIn domains in its troubleshooting help. |
| Microsoft UET | https://bat.bing.com in script-src for the library, and in img-src and connect-src for event requests (Microsoft documents events going to bat.bing.com/action/). |
Google's full, current list is in its Content Security Policy guide for Google tags. Note that Google asks you to list country-specific Google domains individually, because CSP does not allow a wildcard top-level domain.
For platform-specific symptoms beyond CSP, see Meta Pixel not firing, TikTok Pixel not firing and LinkedIn Insight Tag not working.
Step 5: Allow the inline base code safely
You have three options for inline base code. In order of preference:
- Nonce. Your server generates a fresh random value on every response, adds
'nonce-VALUE'to the script directive and setsnonce="VALUE"on the inline script. Google recommends this for the GTM container snippet, and Tag Manager then propagates the nonce to scripts it adds. - Hash. Add the SHA-256 hash of the exact inline script. This works for static snippets, but any whitespace or ID change breaks it.
'unsafe-inline'. Allows all inline scripts. It defeats much of the purpose of the policy, and Google describes it as discouraged.
GTM Custom JavaScript variables evaluate to undefined under a CSP unless script-src includes 'unsafe-eval'. Google advises using it only when absolutely necessary and suggests Custom Templates instead. If a variable mysteriously returns undefined after a CSP rollout, this is a likely cause.
Step 6: Test with Content-Security-Policy-Report-Only
Do not test a stricter or changed policy directly in production. Send it as Content-Security-Policy-Report-Only first. The browser evaluates the policy and reports violations in the console and to your reporting endpoint, but blocks nothing.
Reporting-Endpoints: csp-endpoint="https://example.com/csp-reports"
Content-Security-Policy-Report-Only: script-src 'self' 'nonce-abc123'
https://connect.facebook.net; connect-src 'self' https://www.facebook.com;
img-src 'self' https://www.facebook.com; report-to csp-endpoint
Things to know about report-only mode, from MDN's reference:
- It only works as an HTTP response header, not in a
<meta>tag. - Reports are sent only if you specify a reporting directive.
report-tois the modern option;report-uriis deprecated but still used for wider browser support, so many sites send both. - You can send an enforced policy and a report-only policy at the same time, which lets you trial a change while the current policy stays in force.
Run report-only for long enough to cover your real traffic: every page template, checkout steps, consent states and each platform's events. Then promote it to an enforced policy.
How to verify the fix
- Reload with the console open and confirm no CSP errors mention your pixel hosts.
- In the Network tab, confirm the vendor library loads and that event requests return a success status, not
(blocked:csp). - Trigger a test conversion and check it in the platform's test tool, such as Meta Test Events or GA4 DebugView.
- Repeat on checkout or other pages that may have their own, stricter policy.
FAQ
Why does my pixel work in GTM preview but not for real visitors?
Preview mode needs its own hosts, so teams sometimes allow extra domains while debugging. Compare the policy on a normal page load with what you saw in preview, and check whether a CDN applies a different policy to cached pages. If preview itself fails, see GTM preview mode not working.
Can I just allow https: in script-src?
It would unblock the pixels, but it also allows scripts from any HTTPS host, which removes most of the protection CSP provides. List specific hosts instead.
Does CSP affect server-side events?
No. CSP is enforced by the browser on the page. Events your server sends directly to a conversions API never pass through it.
The console shows no CSP errors, but the pixel still fails. What now?
Then CSP is probably not the cause. Look for ad blockers, consent tools holding tags back, or JavaScript errors earlier in the page.