sitekits.dev
press ⌘K to switch tools
CSP

Content Security Policy Generator

Generate Content-Security-Policy headers.

local
csp-generator

Build a Content-Security-Policy. Common keywords: 'self' 'none' 'unsafe-inline' 'unsafe-eval' 'strict-dynamic' data: https: blob:

§01 ABOUT THIS TOOL

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

  1. 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.
  2. Add the origins you actually need, one directive at a time. Separate multiple sources with spaces.
  3. Leave a directive blank to omit it; blank directives fall back to default-src.
  4. Copy the header and deploy it as Content-Security-Policy-Report-Only first. Read the violation reports, fix the policy, then switch to the enforcing header name.

The source keywords

KeywordMeaning
'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.

FAQ
Does this validate my policy?
No. It assembles the header text from the fields you fill in; it does not check that your sources are reachable or that your site still works. The only reliable validation is Report-Only mode on your real site, described below.
Why does 'unsafe-inline' get ignored when I also add a hash?
That is the spec, not a bug. If a script-src contains any hash or nonce source, browsers that understand them ignore 'unsafe-inline' entirely. It is a deliberate upgrade path — old browsers get the permissive keyword, new ones get the strict list.
What is the difference between frame-src and frame-ancestors?
frame-src controls what your page may embed. frame-ancestors controls who may embed your page — it is the CSP replacement for X-Frame-Options, and unlike most directives it is not covered by default-src.
Do I need default-src if I list every other directive?
It is still worth setting. default-src is the fallback for the fetch directives you did not write, including ones added to the spec after you shipped. Setting it to 'self' means a future directive fails closed rather than open.
Where do I put the finished header?
As an HTTP response header, ideally at the CDN or web server so it applies to every response. A <meta http-equiv> tag also works but cannot express frame-ancestors or report-uri, and it only takes effect once the parser reaches it.