← All tools

CSP Builder

Build a Content Security Policy interactively. Start from a template, adjust directives, see the policy update live, and copy a config snippet for Nginx, Apache, or Cloudflare.

Start from:

Generated policy

Fix it

Where: Add inside your server block — e.g. /etc/nginx/sites-available/yoursite. Reload: sudo nginx -s reload
Where: Add to .htaccess in your site root, or to your VirtualHost config. Requires mod_headers.
Where: Apply via Cloudflare Dashboard. See snippet for navigation path.
REFERENCE

Understanding Content Security Policy

A practical guide to deploying CSP without breaking your site.

What is Content Security Policy?

Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of content — scripts, styles, images, fonts, iframes, and more — are allowed to load on a page. When properly configured, it dramatically reduces the impact of cross-site scripting (XSS), one of the most common and damaging web vulnerabilities.

Unlike a single-purpose header like X-Frame-Options (which only controls iframing), CSP is a complete declarative policy: you list the trusted sources for each kind of resource, and the browser blocks anything that doesn't match. If an attacker manages to inject a <script> tag pointing to their own server, CSP prevents the browser from executing it.

CSP is supported by all modern browsers and has been the OWASP-recommended primary defense against XSS for over a decade.

Why CSP is hard to deploy

CSP is powerful, but it's notoriously tricky to roll out without breaking your site. Most pages depend on dozens of third-party resources — analytics, fonts, error tracking, embedded videos, payment widgets — and missing any single one in your policy will silently break it. There are two practical strategies:

Report-only first. Deploy Content-Security-Policy-Report-Only instead of the enforcing header. The browser will check your policy but won't block anything; instead, it'll log violations (and optionally POST them to a report-uri endpoint). After a few days of real traffic, you'll know exactly what your policy is missing.

Start broad, tighten over time. Begin with a permissive policy (default-src 'self' https:) and progressively restrict. Each release, narrow one directive based on what your site actually needs.

The CSP Builder above gives you a starting policy. The Headers Checker will then tell you whether what you shipped is actually being delivered by your server.

Directive-by-directive guide

CSP is made up of directives — individual rules for different resource types. Here are the most important ones:

default-src is the fallback. Any fetch directive you don't explicitly set inherits from default-src. Setting it to 'self' is a reasonable safe baseline; setting it to 'none' (GitHub's choice) is the strictest possible default and forces you to explicitly allow each resource type.

script-src controls where JavaScript can come from. This is the most security-critical directive. Avoid 'unsafe-inline' and 'unsafe-eval' — they essentially turn off CSP's XSS protection. If you need inline scripts, use nonces ('nonce-abc123') or hashes ('sha256-...').

style-src controls stylesheets. Many modern frameworks (Tailwind, CSS-in-JS libraries) inject inline styles, so 'unsafe-inline' here is extremely common and significantly less dangerous than in script-src — attackers can't execute code through CSS, only modify appearance.

img-src controls images. Most sites use 'self' data: https: to allow same-origin images, data URIs (for icons/SVGs), and any HTTPS source (for user-uploaded content or CDNs).

font-src controls fonts. Common values include 'self' and font CDNs like https://fonts.gstatic.com.

connect-src controls where your page can make network requests — fetch(), XMLHttpRequest, WebSocket, EventSource. If your app talks to APIs, this is where you list those origins.

frame-src controls iframes that your page embeds (Stripe checkout, YouTube embeds, Google Maps).

frame-ancestors is the reverse: it controls who can embed your page in an iframe. This is the modern replacement for X-Frame-Options. Set it to 'none' to prevent any framing, or 'self' to allow same-origin only.

form-action restricts where forms on your page can submit. Setting it to 'self' prevents phishing attacks where injected forms post user data to attacker-controlled servers.

base-uri restricts URLs in the <base> element. Most sites should set this to 'self' to prevent attackers from changing relative URLs by injecting a <base> tag.

object-src controls <object>, <embed>, and <applet> (Flash-era plugins). Setting it to 'none' is recommended for almost every modern site.

Common source keywords

CSP source values can be exact URLs, wildcards, or special keywords:

You can also use scheme-host wildcards like https://*.example.com to allow all subdomains.

Frequently asked questions

Will CSP break my Google Analytics?

Possibly. If you load gtag.js from https://www.googletagmanager.com, you'll need to allow that in script-src. Google Analytics also makes XHR requests to https://www.google-analytics.com, so add that to connect-src. The CSP Builder's "Balanced" template handles common patterns like this.

What's the difference between Content-Security-Policy and Content-Security-Policy-Report-Only?

The enforcing header blocks resources that violate the policy. The report-only header logs violations to the browser console (and optionally to a report-uri endpoint) but doesn't block anything. Use report-only mode while testing your policy, then switch to enforcing once you're confident.

Can I use both headers at the same time?

Yes, and it's a useful pattern. Ship an enforcing header with your known-good policy, and ship a stricter report-only header to find places to tighten further. Browsers block based on the first and log based on the second.

Do I need X-Frame-Options if I have frame-ancestors in my CSP?

CSP frame-ancestors supersedes X-Frame-Options in browsers that support both (all modern browsers). For maximum compatibility, set both, but if you have to pick one, frame-ancestors is more flexible.

How do nonces work?

A nonce is a random value generated per-request. You include it in your CSP (script-src 'nonce-abc123') and in the nonce attribute of every inline <script nonce="abc123">. The browser only executes inline scripts whose nonce matches the header. Since the nonce changes every request, attackers can't predict it.

Further reading