Skip to content
ToolBoxGeniehome

CSP Header Generator

Developer Tools · Added

A Content-Security-Policy tells the browser what your page is allowed to load and run, which is what turns an injected script from a breach into a console error. This builds one directive by directive, checks it for the settings that make a policy decorative rather than protective, and gives you the header in the form your server wants it.

Start from

Starting points, not recommendations — a policy is a statement about one site’s dependencies, and only you know what yours loads.

The fallback for every fetch directive that is not set

JavaScript: script elements, event handlers, eval

Stylesheets and inline style blocks

Images, including favicons and CSS backgrounds

Webfonts loaded by @font-face

fetch, XHR, WebSocket, EventSource, sendBeacon

audio and video elements

object, embed and applet. Almost always 'none'

What may be loaded into an iframe on your page

Web workers, shared workers and service workers

The web app manifest

Who may frame you. This is what stops clickjacking

Where a form on your page may submit to

What a base element may set. 'self' or 'none' stops a hijack

Names a group defined by a Reporting-Endpoints header. Without that header it does nothing.

Your policy

Content-Security-Policy-Report-Only: default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; upgrade-insecure-requests
Directives
11
Critical issues
0

Nothing that defeats the policy

Warnings
0
  • Note · Content-Security-Policy-Report-OnlyReport-only mode blocks nothing. It is the right way to start — deploy it, watch the reports for a week of real traffic, then switch the header name once they are quiet.

Setting the header

add_header Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data:; font-src 'self'; connect-src 'self'; object-src 'none'; frame-ancestors 'none'; form-action 'self'; base-uri 'self'; upgrade-insecure-requests" always;

As a meta tag

A meta tag cannot be report-only — that mode exists only as a header.

Every warning above comes from reading the policy, not from testing it: this page never fetches your site and cannot know what your pages actually load. A policy that looks right here can still break something, which is what report-only mode is for — deploy it, watch real traffic for a week, then change the header name once the reports are quiet. Nothing you type is sent anywhere.

How to use the csp header generator

  1. 1Start from one of the three presets, or from an empty policy.
  2. 2Fill in the directives your site needs. The keyword buttons under each field add the quoted values correctly.
  3. 3Read the warnings — critical ones mean the policy does not do what it looks like it does.
  4. 4Copy the header, or the server snippet, and deploy it in report-only mode first.

Examples

A policy that looks strict and is not

Input
script-src 'self' 'unsafe-inline'
Result
Critical: 'unsafe-inline' allows exactly what cross-site scripting needs

The single most common way a CSP ends up protecting nothing.

A quoting mistake

Input
default-src self
Result
Critical: self must be written 'self' — unquoted, the browser reads it as a hostname

A directive a meta tag cannot carry

Input
frame-ancestors 'none'
Result
The meta tag is withheld, with the reason: browsers ignore frame-ancestors there. Send it as a header

About the csp header generator

What a CSP is actually defending

The threat model is straightforward: an attacker finds a way to get markup into your page — a comment field, a URL parameter reflected into HTML, a third-party widget — and uses it to run JavaScript in your users' sessions. Everything that session can do, the script can do.

A Content-Security-Policy makes the browser the enforcement point. The injection may still land in the HTML, but if the policy says scripts may only come from your own origin and carry a nonce the attacker cannot guess, the browser refuses to run it and reports the attempt. That is a much stronger position than trying to sanitise every input path perfectly, because it does not depend on getting every path right.

The directives people forget

Three directives do not fall back to default-src, so leaving them out leaves them unrestricted no matter how strict the rest of the policy is. frame-ancestors controls who may put your site in an iframe and is the modern replacement for X-Frame-Options; without it, clickjacking is available to anyone. base-uri limits what a base element may set, and an injected base element repoints every relative URL on the page, script sources included. form-action controls where your forms may submit, which is how an injected form exfiltrates a password manager's autofill.

object-src does fall back to default-src, but it is worth setting to 'none' explicitly. Plugins are gone from browsers, nothing legitimate needs it any more, and object and embed elements remain a route worth closing by name rather than by inheritance.

Why a policy usually gets weaker after deployment

The pattern is familiar: a strict policy goes out, something breaks, and the fastest fix is to add a source to the directive that blocked it. Do that a few times and the policy allows https: for scripts, which allows every CDN that hosts a copy of a library capable of running arbitrary code, which is most of them.

The way out is to treat each break as a question about the site rather than about the policy. A blocked inline handler usually means an event handler that belongs in a script file. A blocked CDN usually means a dependency nobody chose deliberately. The policy is a fairly accurate inventory of what a page loads, and the parts of it that feel unreasonable are often the parts of the page that are.

Frequently asked questions

Why is 'unsafe-inline' such a problem?
Because a Content-Security-Policy exists mainly to make cross-site scripting unexploitable, and inline script is what cross-site scripting injects. A policy that allows inline script has allowed the attack it was deployed to stop. The fix is a nonce — a random value your server generates for each response and puts on both the header and your own script tags — or a hash of each inline script's contents.
What does 'strict-dynamic' do?
It extends trust from the scripts you explicitly allowed to the scripts they load. That solves the practical problem with nonces: a bundler or a tag manager loads further scripts at runtime, and you cannot put a nonce on a tag you did not write. Browsers that support it ignore host allow-lists and 'unsafe-inline' entirely, which is why the modern pattern keeps those in the policy purely as a fallback for older browsers. On its own, with no nonce or hash beside it, 'strict-dynamic' allows nothing and will break your page.
Should I use the header or the meta tag?
The header, wherever you can set one. A meta tag works for the common fetch directives but browsers ignore frame-ancestors, report-uri, report-to and sandbox there, and it only applies to resources the parser reaches after it. The generator withholds the meta tag when your policy contains something it cannot carry, rather than handing you markup that silently drops a directive.
What is report-only mode for?
Deploying a policy without breaking the site. With the Content-Security-Policy-Report-Only header the browser blocks nothing and reports what it would have blocked, so you can see the real cost of a policy against real traffic before it bites. The usual sequence is report-only for a week or two, fix what shows up, then change the header name.
Does this check my site?
No, and it cannot. It reads the policy you have typed and nothing else — it makes no request to your domain and has no idea what your pages actually load. Every warning here is about the policy text. Whether the policy fits your site is what the report-only deployment answers.