Security Glossary

Content Security Policy (CSP)

Content Security Policy is a security standard implemented via HTTP headers that allows web applications to declare which content sources are trusted, providing granular control over script execution, resource loading, and frame embedding.

Understanding Content Security Policy (CSP)

CSP is the most powerful browser-enforced security header available. It provides fine-grained control over every type of resource a page can load. The policy is specified through directives, each controlling a resource type: script-src for JavaScript, style-src for CSS, img-src for images, connect-src for AJAX/fetch/WebSocket, font-src for fonts, frame-src for iframes, and many more.

A strict CSP typically starts with default-src 'none' (block everything by default) and then allowlists specific sources for each resource type. This deny-by-default approach ensures that any new resource types are automatically blocked unless explicitly permitted. The most security-critical directive is script-src, which controls JavaScript execution.

Nonce-based CSP is the recommended modern approach. A cryptographically random nonce (number used once) is generated for each page request, added to the CSP header, and included as an attribute on each legitimate script tag. Only scripts with the matching nonce execute. This approach is more secure than domain-based whitelisting because it prevents attacks that abuse whitelisted CDN endpoints to host malicious scripts.

The frame-ancestors directive deserves special attention — it replaces X-Frame-Options and controls which domains can embed your page in an iframe. Unlike other CSP directives, frame-ancestors cannot be set in a meta tag and must be delivered via the HTTP header. Setting frame-ancestors 'none' is the CSP equivalent of X-Frame-Options: DENY.

Why This Matters for Vibe-Coded Apps

Implementing CSP on vibe-coded applications is challenging because AI-generated code often loads resources from many external sources: analytics scripts, font CDNs, widget libraries, and third-party APIs. Each of these must be whitelisted in the CSP. Start with CSP in report-only mode to discover what your application loads, then build the policy based on those reports.

Next.js applications can set CSP headers in next.config.js or middleware. For nonce-based CSP in Next.js, generate a nonce in middleware and pass it to the page via headers. Vibe App Scanner identifies missing CSP and can help you understand which directives your application needs.

Real-World Examples

GitHub Strict CSP

GitHub deploys one of the most restrictive CSPs of any major website, using nonce-based script allowlisting with strict-dynamic. Their policy prevents XSS exploitation even when injection points are found, as attackers cannot execute unauthorized scripts. GitHub's CSP journey from permissive to strict is well documented in their engineering blog.

CSP Bypass via Whitelisted CDN

Researchers demonstrated that CSP policies whitelisting CDN domains like cdnjs.cloudflare.com can be bypassed by finding old or obscure library versions on the CDN that contain useful gadgets for executing arbitrary code. This research drove the adoption of nonce-based CSP over domain-based whitelisting.

Stripe CSP Implementation

Stripe implements a strict CSP on their dashboard that uses nonces and hashes for inline scripts. Their approach demonstrates how a complex web application with many interactive features can maintain a strong CSP through careful architecture and nonce propagation.

Frequently Asked Questions

How do I deploy CSP without breaking my site?

Start with Content-Security-Policy-Report-Only instead of Content-Security-Policy. This logs violations without blocking anything. Add a report-uri or report-to directive to collect violation reports. Analyze the reports to understand what resources your site loads. Build your policy based on these reports. Test thoroughly in staging. Then switch from Report-Only to enforced CSP. Keep the reporting active in production to catch new violations.

What is strict-dynamic and when should I use it?

strict-dynamic tells the browser that scripts loaded by trusted scripts should also be trusted. If a nonce-trusted script uses createElement to add another script tag, the dynamically loaded script is automatically allowed. This is essential for applications that use code splitting, dynamic imports, or third-party loaders. Without strict-dynamic, you would need to whitelist every dynamically loaded script individually, which is impractical for modern bundled applications.

Can CSP be set via meta tags?

Yes, using <meta http-equiv="Content-Security-Policy" content="...">, but with limitations. Meta tag CSP cannot use frame-ancestors (clickjacking protection), report-uri/report-to (violation reporting), or sandbox directives. Meta tag CSP is also easier for attackers to bypass through early DOM injection. The HTTP header delivery method is preferred because it is enforced before any page content is parsed.

Why does my CSP block inline styles?

By default, CSP blocks inline styles just like inline scripts. If your style-src does not include 'unsafe-inline', inline style attributes and <style> tags are blocked. You can allow inline styles with 'unsafe-inline' (less secure but common), nonces (adding a nonce to each <style> tag), or hashes (whitelisting specific inline style content). Many applications accept 'unsafe-inline' for styles since inline style injection is significantly less dangerous than inline script injection.

Is Your App Protected?

VAS automatically scans for vulnerabilities related to content security policy (csp) and provides detailed remediation guidance. Our scanner targets issues common in AI-generated applications.

Scans from $5, results in minutes. Get actionable fixes tailored to your stack.

Get Starter Scan