Content Security Policy Generator
Generate Content-Security-Policy headers.
Build a Content-Security-Policy. Common keywords: 'self' 'none' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' data: https: blob:
Overview
A Content-Security-Policy tells the browser which sources it may load resources from. Its practical value is narrower than the name suggests: CSP does not fix cross-site scripting, it contains it. If an injection slips through your output encoding, a strict policy is what stops the injected script from executing or from sending what it stole to an attacker’s server.
The header is a list of directives separated by semicolons, each naming a resource type and the sources allowed for it. This tool gives you one field per directive, fills in a sensible starting policy, and shows the assembled header as you type.
How to use
- Start from the pre-filled policy —
default-src 'self',object-src 'none',base-uri 'self',frame-ancestors 'none'. That is a reasonable floor for a site that serves its own assets. - Add the origins you actually need, one directive at a time. Separate multiple sources with spaces.
- Leave a directive blank to omit it; blank directives fall back to
default-src. - Copy the header and deploy it as
Content-Security-Policy-Report-Onlyfirst. Read the violation reports, fix the policy, then switch to the enforcing header name.
The source keywords
| Keyword | Meaning |
|---|---|
'self' | The document’s own origin — same scheme, host and port |
'none' | Nothing at all. Only meaningful as the sole source |
'unsafe-inline' | Allow inline <script> / <style> and inline event handlers |
'unsafe-eval' | Allow eval, new Function, and string arguments to setTimeout |
'strict-dynamic' | Trust scripts created by an already-trusted script. Makes host allowlists in that directive be ignored |
data: | Allow data: URIs. Reasonable for img-src, dangerous for script-src |
https: | Any origin over HTTPS. Broad — usually a sign the policy needs narrowing |
blob: | Allow blob: URLs, needed for Web Workers created from generated code |
Hashes ('sha256-…') and nonces ('nonce-…') are the strict alternatives to
'unsafe-inline'. A hash covers a specific inline script whose content never
changes, which suits statically generated sites. A nonce is a fresh random
value per response, which suits server-rendered pages. You cannot use a nonce
on a static file host, because there is no per-request step to generate one.
The directives that are easy to get wrong
base-uri is not covered by default-src. Without it, an injected
<base href> tag can redirect every relative URL on the page to an attacker’s
host — including your script tags — without ever injecting a script itself.
Set it to 'self' or 'none'.
form-action is also not covered by default-src. Without it, an injected
form can post your users’ input to another origin.
frame-ancestors replaces X-Frame-Options. Where the two disagree,
modern browsers follow CSP, so setting only the old header leaves newer
browsers unconstrained by anything you meant to say.
object-src 'none' is worth setting explicitly. Plugin content is a
legacy execution path with no legitimate use on a new site.
connect-src is the one people forget. It governs fetch, XHR,
WebSockets and sendBeacon — which is exactly how an injected script would
exfiltrate data. A tight script-src with a wide-open connect-src leaves
the door it was meant to close.
Examples
A static site serving its own assets with no third-party scripts:
default-src 'self'; base-uri 'self'; object-src 'none'; frame-ancestors 'none';
form-action 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:;
font-src 'self'; connect-src 'self'; upgrade-insecure-requests
The same site plus an analytics tag, allowing only the specific origins it
needs rather than https::
script-src 'self' https://www.googletagmanager.com;
connect-src 'self' https://www.google-analytics.com
Collecting violation reports
Report-Only mode is only useful if you read the reports. Two mechanisms exist, and browser support is split, so a rollout usually sends both.
report-uri /csp-report is the older directive. It is deprecated but still the
one with the widest support, and it posts a JSON body describing the blocked
resource, the directive that blocked it, and the document URL.
report-to is the replacement. It names a reporting group configured by a
separate Reporting-Endpoints response header, which lets one endpoint serve
CSP, deprecation and intervention reports together.
Whichever you use, expect noise. Browser extensions inject scripts and styles
into pages, and their violations look identical to yours in the report. The
signal is a violation that appears for many distinct users on the same
document URL and the same directive; a one-off blocked-uri pointing at a
chrome-extension: scheme is somebody’s password manager.
Neither directive is available in a <meta> tag. Reporting requires the real
HTTP header.
Notes
upgrade-insecure-requests rewrites http:// subresource requests to
https:// before they are made. It is a migration aid for pages with mixed
content, not a substitute for fixing the URLs, and it does not apply to
navigation to other sites.
style-src is usually the last 'unsafe-inline' to go, and often the one
worth keeping. Inline style attributes cannot be covered by a hash — you
would need 'unsafe-hashes' plus a hash for every distinct attribute value —
and CSS injection is a much narrower problem than script injection. Spending
the effort on script-src first is the better trade.
Deploy with Content-Security-Policy-Report-Only before enforcing. A policy
that is one directive too strict does not degrade gracefully: it breaks the
page silently for every visitor whose browser enforces it, and you will find
out from users rather than from your own testing.
If you want to check what a live site currently sends, the HTTP header checker shows the response headers for any URL.