BLOG
2026-04-09
10 min read

How to Fix a Missing Content-Security-Policy (CSP) Header

Step-by-step guide to adding a Content-Security-Policy header to your website. Covers Next.js, Express, Nginx, Apache, and Cloudflare with copy-paste configs.

CSPsecurity-headershow-toweb-security

What is Content-Security-Policy?

Content-Security-Policy (CSP) is an HTTP response header that tells browsers which resources are allowed to load on your page. It's the single most effective defense against Cross-Site Scripting (XSS) attacks.

Without CSP, an attacker who finds an injection point can load any script from any domain. With a strict CSP, even if they find an injection point, the browser blocks the malicious script.

How to Check if Your CSP is Missing

The fastest way: run a free ScanMyVibe scan. It checks your CSP header and reports exactly what's missing, with an AI-generated fix prompt for your specific framework.

You can also check manually by running curl against your site and grepping for the content-security-policy header. If nothing shows up — your site has no CSP.

Step 1: Define Your Policy

Start with a strict baseline and loosen as needed. A good starting policy:

->default-src 'self' — Only allow resources from your own domain
->script-src 'self' — Only allow scripts from your domain (blocks inline scripts and external CDNs)
->style-src 'self' 'unsafe-inline' — Allow your styles + inline styles (needed for most CSS-in-JS)
->img-src 'self' data: — Allow images from your domain + data URIs
->font-src 'self' — Only allow fonts from your domain
->connect-src 'self' — Only allow fetch/XHR to your domain
->frame-ancestors 'none' — Prevent your site from being embedded in iframes (clickjacking protection)

Step 2: Add External Sources

If you use third-party services, add their domains to the relevant directives. For example, Google Analytics needs its domain in script-src and connect-src. Google Fonts needs googleapis.com in style-src and gstatic.com in font-src.

Step 3: Implement by Framework

Next.js (App Router)

Add a headers() function to your next.config.ts. Return an array with the Content-Security-Policy header for all routes using the source pattern '/(.*)'. Join all directives with semicolons.

Express.js

Use the helmet middleware with contentSecurityPolicy. Pass your directives as an object with arrays of allowed sources per directive.

Nginx

Add the header in your server block using add_header Content-Security-Policy followed by your policy string and the 'always' flag.

Apache (.htaccess)

Use Header always set Content-Security-Policy followed by your policy string in quotes.

Cloudflare (Transform Rules)

Go to Rules > Transform Rules > Modify Response Header. Add the Content-Security-Policy header name and your policy as the value. Apply to all requests.

Step 4: Test with Report-Only Mode

Before enforcing, use Content-Security-Policy-Report-Only instead. This logs violations without blocking them. Check your logs for unexpected blocks, add the missing domains, then switch to the enforcing Content-Security-Policy header.

Step 5: Verify

After deploying, run another ScanMyVibe scan to confirm your CSP is working correctly. The scanner will flag any remaining issues like overly permissive directives.

Common Mistakes to Avoid

->Using 'unsafe-inline' for scripts — defeats the entire purpose of CSP. Use nonces or hashes instead.
->Using 'unsafe-eval' — allows eval-based XSS attacks.
->Forgetting frame-ancestors — leaves you open to clickjacking attacks.
->Not testing in report-only first — can break your site in production by blocking required resources.
->Setting default-src to wildcard — allows everything, making CSP completely useless.

Summary

Adding CSP takes 15 minutes and blocks the most common XSS attack vectors. Start with a strict policy, test in report-only mode, add exceptions for your third-party services, then enforce. Use ScanMyVibe to verify your implementation is correct.