A Content Security Policy (CSP) is an HTTP response header that tells the browser which sources of scripts, styles, images, and other resources are allowed to load — the single most effective defence against cross-site scripting and content injection. This builder lets you fill in each directive, explains what it controls, warns about weak values, and assembles a correctly formatted header you can paste straight into your server config. Nothing is transmitted.
How it works
CSP is a single header whose value is a semicolon-separated list of directives, each followed by a space-separated source list:
Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; frame-ancestors 'none'
The builder follows the spec’s rules:
- Keyword sources (
'self','none','unsafe-inline','unsafe-eval', nonces, hashes) are single-quoted. - Host and scheme sources (
https://cdn.example.com,data:) are written bare. - Directives you leave empty are omitted, so the header stays minimal.
- Each non-empty directive is joined with
;to form the final header string.
It also flags risk: adding 'unsafe-inline' to script-src triggers a warning because it undermines the policy.
Directive reference at a glance
| Directive | Controls | Common values |
|---|---|---|
default-src | Fallback for all fetch directives | 'self' |
script-src | JavaScript sources | 'self' 'nonce-abc123' |
style-src | CSS sources | 'self' https://fonts.googleapis.com |
img-src | Image sources | 'self' data: https://images.cdn.com |
font-src | Font sources | 'self' https://fonts.gstatic.com |
connect-src | Fetch, XHR, WebSocket | 'self' https://api.example.com |
frame-ancestors | Which pages may embed you | 'none' or 'self' |
report-uri | Where to send violation reports | your endpoint URL |
A worked example
Suppose you run a blog that loads scripts only from your own origin, images from an S3 bucket, and fonts from Google Fonts, with no inline scripts anywhere. A minimal policy might read:
Content-Security-Policy:
default-src 'none';
script-src 'self';
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' https://my-bucket.s3.amazonaws.com;
connect-src 'self';
frame-ancestors 'none'
Starting from default-src 'none' means every directive you omit defaults to blocking all sources — a stronger baseline than 'self'. You then open up exactly what you need, keeping the attack surface as small as possible.
Common mistakes and how to avoid them
Over-relying on 'unsafe-inline' is the most frequent error. It allows any inline <script> or <style> tag on the page, which is exactly what XSS exploits inject. Switch to nonces: your server generates a random value each request, adds 'nonce-RANDOM' to script-src, and puts nonce="RANDOM" on every legitimate script tag. The browser then only executes scripts bearing a matching nonce — injected scripts get none.
Forgetting frame-ancestors leaves you open to clickjacking even when the rest of the policy is tight. frame-ancestors 'none' replicates the old X-Frame-Options: DENY but at higher precedence.
Deploying without a report endpoint means you will not know about legitimate resources you missed until users complain. Point report-uri or the newer report-to directive at a collector (even a small log endpoint works) and watch reports for a week before switching from Report-Only to enforcing mode.
Tips and notes
- Start strict with
default-src 'self', then open up only the specific directives and hosts your app actually needs. - Prefer nonces or hashes over
'unsafe-inline'for any inline script or style you cannot remove. - Always set
frame-ancestorsto stop clickjacking;'none'blocks all framing, or list trusted origins. - Test with the
Content-Security-Policy-Report-Onlyheader first so violations are reported without breaking the page, then switch to the enforcing header.