Fix Content Security Policy (CSP) Violations | OpsBlu Docs

Fix Content Security Policy (CSP) Violations

Diagnose and fix CSP violations that block scripts, styles, and resources on your website

What This Means

Content Security Policy (CSP) is a security header that controls which resources can load on your website. CSP violations occur when a resource is blocked because it doesn't match the policy rules. This can prevent scripts (including analytics), styles, images, and third-party integrations from working.

Impact on Your Business

Analytics and Tracking:

Website Functionality:

  • Third-party widgets won't load
  • Embedded content (videos, maps) may be blocked
  • Payment processors might not function
  • Chat widgets and support tools fail

Security Benefits (When Configured Correctly):

  • Prevents cross-site scripting (XSS) attacks
  • Blocks unauthorized script injection
  • Protects against data exfiltration
  • Mitigates clickjacking attacks

How to Diagnose

Method 1: Browser Console (Primary)

  1. Open your website
  2. Open DevTools (F12)
  3. Navigate to Console tab
  4. Look for CSP errors:
Refused to load the script 'https://www.googletagmanager.com/gtm.js'
because it violates the following Content Security Policy directive:
"script-src 'self'".

What to Look For:

  • "Refused to load the script" - Script blocked
  • "Refused to load the stylesheet" - CSS blocked
  • "Refused to connect to" - API/fetch blocked
  • The directive name that blocked it (script-src, style-src, etc.)

Method 2: Network Tab

  1. Open DevTools → Network tab
  2. Reload the page
  3. Look for blocked requests (red or canceled)
  4. Check Status column for "(blocked:csp)"

Method 3: Security Tab

  1. Open DevTools → Security tab
  2. Review the security overview
  3. Click "View certificate" for details

Method 4: CSP Evaluator

  1. Navigate to Google CSP Evaluator
  2. Enter your CSP header value
  3. Review findings and recommendations

Method 5: Check Response Headers

// View CSP header in console
fetch(window.location.href)
  .then(res => {
    console.log('CSP:', res.headers.get('content-security-policy'));
    console.log('CSP Report Only:', res.headers.get('content-security-policy-report-only'));
  });

Or check in DevTools → Network → Select main document → Headers tab.

Understanding CSP Directives

Common Directives

Directive Controls Example Blocked Resource
script-src JavaScript files GTM, GA4, third-party scripts
style-src CSS stylesheets Font libraries, widget styles
img-src Images External images, tracking pixels
connect-src Fetch/XHR/WebSocket API calls, analytics beacons
font-src Web fonts Google Fonts, custom fonts
frame-src Iframes YouTube embeds, payment forms
media-src Audio/video Hosted media files
object-src Plugins (Flash, Java) Legacy embeds
default-src Fallback for all Anything not specifically defined

Common Sources

Source Meaning
'self' Same origin only
'unsafe-inline' Inline scripts/styles allowed
'unsafe-eval' eval() and similar allowed
'none' Block everything
https: Any HTTPS source
data: Data URIs allowed
blob: Blob URLs allowed
*.domain.com Wildcard subdomain
https://specific.com Specific domain
'nonce-abc123' Scripts with matching nonce
'sha256-...' Scripts with matching hash

General Fixes

Fix 1: Add Required Domains to CSP

For Google Analytics and GTM:

Content-Security-Policy:
  script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com https://ssl.google-analytics.com;
  img-src 'self' https://www.google-analytics.com https://www.googletagmanager.com;
  connect-src 'self' https://www.google-analytics.com https://analytics.google.com https://region1.google-analytics.com;

For Meta Pixel:

Content-Security-Policy:
  script-src 'self' https://connect.facebook.net;
  img-src 'self' https://www.facebook.com https://www.facebook.com/tr/;
  connect-src 'self' https://www.facebook.com;

For Google Fonts:

Content-Security-Policy:
  style-src 'self' https://fonts.googleapis.com;
  font-src 'self' https://fonts.gstatic.com;

Fix 2: Complete CSP for Common Analytics Stack

Content-Security-Policy:
  default-src 'self';
  script-src 'self' 'unsafe-inline' 'unsafe-eval'
    https://www.googletagmanager.com
    https://www.google-analytics.com
    https://ssl.google-analytics.com
    https://connect.facebook.net
    https://www.googleadservices.com
    https://googleads.g.doubleclick.net;
  style-src 'self' 'unsafe-inline'
    https://fonts.googleapis.com
    https://tagmanager.google.com;
  img-src 'self' data:
    https://www.google-analytics.com
    https://www.googletagmanager.com
    https://www.facebook.com
    https://www.google.com
    https://www.google.com/ads
    https://googleads.g.doubleclick.net;
  font-src 'self'
    https://fonts.gstatic.com;
  connect-src 'self'
    https://www.google-analytics.com
    https://analytics.google.com
    https://region1.google-analytics.com
    https://stats.g.doubleclick.net
    https://www.facebook.com;
  frame-src 'self'
    https://www.google.com
    https://www.youtube.com
    https://www.facebook.com;

Fix 3: Use Report-Only Mode for Testing

Test CSP changes without breaking your site:

Content-Security-Policy-Report-Only:
  default-src 'self';
  script-src 'self' https://www.googletagmanager.com;
  report-uri https://your-report-endpoint.com/csp;

This logs violations without blocking resources.

Fix 4: Add Nonces for Inline Scripts

Server-side (generate unique nonce per request):

<!-- In your HTML, with server-generated nonce -->
<script nonce="abc123xyz">
  // Inline script content
  gtag('config', 'G-XXXXXXXXXX');
</script>

CSP header:

Content-Security-Policy: script-src 'self' 'nonce-abc123xyz';

Fix 5: Platform-Specific Configuration

Apache (.htaccess):

Header set Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com;"

Nginx:

add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://www.googletagmanager.com;";

Node.js/Express:

const helmet = require('helmet');

app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'", "https://www.googletagmanager.com"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", "https://www.google-analytics.com"],
    connectSrc: ["'self'", "https://www.google-analytics.com"]
  }
}));

Netlify (netlify.toml):

[[headers]]
  for = "/*"
  [headers.values]
    Content-Security-Policy = "default-src 'self'; script-src 'self' https://www.googletagmanager.com;"

Vercel (vercel.json):

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Content-Security-Policy",
          "value": "default-src 'self'; script-src 'self' https://www.googletagmanager.com;"
        }
      ]
    }
  ]
}

Fix 6: Use Meta Tag Fallback

When you can't set HTTP headers:

<meta http-equiv="Content-Security-Policy"
      content="default-src 'self'; script-src 'self' https://www.googletagmanager.com;">

Note: Meta tag CSP has limitations:

  • Cannot use report-uri
  • Cannot use frame-ancestors
  • Must be in <head> early

Platform-Specific Guides

Platform Troubleshooting Guide
Shopify Shopify Troubleshooting
WordPress WordPress Troubleshooting
Webflow Webflow Troubleshooting

Verification

After updating CSP:

  1. Clear browser cache and reload

  2. Check Console for violations:

    • No CSP errors should appear
    • All scripts should load
    • Analytics should function
  3. Test analytics:

    • Check GA4 Realtime reports
    • Verify GTM Preview mode works
    • Test Meta Pixel Helper
  4. Monitor for new violations:

    • Set up CSP reporting endpoint
    • Review reports regularly
    • Update CSP as you add services

Common Mistakes

  1. Too restrictive default-src - Blocks unexpected resources
  2. Missing connect-src - API calls fail silently
  3. Forgetting data: for images - Blocks inline images
  4. Not including 'self' - Blocks your own resources
  5. Using 'unsafe-inline' carelessly - Reduces security
  6. Forgetting wildcard subdomains - Need *.google.com not just google.com
  7. Not testing in Report-Only first - Breaking production
  8. Missing frame-src for embeds - YouTube/Vimeo fail
  9. Ignoring style-src - Widget styles break
  10. Not updating for new services - New tools blocked

Security vs. Functionality Balance

Strictest (Most Secure) ─────────────────────────────> Most Permissive

Strict CSP          Balanced CSP          Permissive CSP
- No inline        - Nonce-based          - 'unsafe-inline'
- Hash-based       - Specific domains     - 'unsafe-eval'
- Specific URLs    - Wildcards limited    - Broad wildcards
- No 'unsafe-*'    - Report violations    - Everything allowed

Recommendation: Start with Report-Only mode to identify all required resources, then implement the strictest policy that allows your site to function.

Additional Resources