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.
Generated policy
Fix it
server block — e.g. /etc/nginx/sites-available/yoursite. Reload: sudo nginx -s reload .htaccess in your site root, or to your VirtualHost config. Requires mod_headers.
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:
'self'— same-origin (matches your protocol, host, and port)'none'— no sources allowed'unsafe-inline'— allows inline<script>and<style>blocks. Avoid inscript-src.'unsafe-eval'— allowseval(),new Function(), and similar. Avoid universally.'strict-dynamic'— modern CSP3 mode that trusts scripts loaded by other trusted scriptsdata:— allowsdata:URIs (inline images, fonts)blob:— allows blob URLs (for dynamically generated content)https:— allows any HTTPS source (broad — use sparingly)*— wildcard. Essentially disables CSP.
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.