CSP builder

Give us the address of a real page. We read its HTML, list every place it loads scripts, styles, images, fonts and frames from, and write a policy you can try in report-only mode, where it can’t break anything.

Pick a typical page, not only the home page: a product page, an article, the contact form. Each one may load from different places.

Or try github.com, wordpress.org, www.bbc.com

A guest list for your own pages

A browser does whatever a page tells it to. If the HTML says “run this script from that server”, it runs. That gets dangerous the day someone else manages to add a line to your page, through a comment field nobody cleaned, a compromised plugin or an ad. The browser has no way of telling their line from yours.

A Content-Security-Policy (CSP) is a response header where you tell the browser, ahead of time, where this page is allowed to load things from. It reads like a guest list, with one line per kind of resource:

default-src 'self';
script-src 'self' https://cdn.example.com;
img-src 'self' data: https://images.example.net;
object-src 'none'

Each line is a directive. 'self' means the page’s own origin. Anything that isn’t on the list gets refused, and the refusal is logged in the browser console.

How this page builds the draft

Our server fetches the address once and parses the HTML the way a browser would, without running any of it. Every address it finds is cut down to its origin (scheme and host, no path) and filed:

DirectiveRead from
script-src<script src>, module preloads, and script addresses named inside inline loader snippets
style-srcstylesheet links and @import rules
img-src, font-srcimage tags, srcset, icons, posters, preloads, and url() in inline CSS and in the first four stylesheets of the site’s own origin
frame-src, media-src, object-src, manifest-srcframes, audio and video, plugins, the manifest link
form-action, base-uriform targets and the <base> tag
connect-srconly preconnect hints and the documented endpoints of a few services found on the page (Google Analytics, Google Fonts, Cloudflare Web Analytics)

The draft starts from default-src 'self', adds one line per directive, and closes the doors that have no fallback: object-src 'none', base-uri 'self', frame-ancestors 'self', plus upgrade-insecure-requests on HTTPS pages. If the page already sends a policy, in a header or a meta tag, we show it and compare it with what the page loads.

The hard part: inline code

Injected code is nearly always inline, as a <script> block or an onclick attribute written into the page. A policy worth having forbids inline code, then. The catch is that most sites have plenty of their own. The tool counts script blocks, event handler attributes, javascript: links, style blocks and style= attributes, then gives you two drafts.

  • The strict draft has no 'unsafe-inline'. It’s where you want to end up, and it would break the inline code counted above.
  • The transitional draft adds 'unsafe-inline' where the page needs it. It still limits which sites things may be loaded from, but it no longer stops injected inline scripts, which is why it’s marked as weak.

You can get from one to the other three ways. Move the code into files, have the server stamp each tag with a per-response nonce, or list the SHA-256 hash of each block in the policy. The tool computes those hashes for the script blocks it found. They only help for blocks whose text never changes.

Report-only first, always

Send the draft as Content-Security-Policy-Report-Only. The browser enforces nothing and logs what it would have blocked. Walk through the site with the console open, add whatever is legitimately missing, and repeat until the log goes quiet. Then rename the header to Content-Security-Policy. You get snippets for Apache, nginx and a meta tag. The meta form can’t do report-only or frame-ancestors.

The draft comes from one page, read once, with nothing executed. It can’t see the requests scripts make after loading (analytics beacons, chat widgets, payment frames, lazy images), what logged-in visitors get, or any other page of the site. Expect to add sources. For a grade of the headers a page sends today, use the security headers grade.

Questions people ask

Will a Content-Security-Policy break my site?

An enforced policy blocks everything it doesn’t list, so a wrong one will. A report-only policy can’t break anything, because all it does is log. Start there, and switch to enforcement once the console shows no violation on your main pages.

Is a policy with unsafe-inline worth having?

Partly. It still restricts where scripts, frames and form posts may go, blocks plugins and stops <base> tag hijacking. It doesn’t stop an attacker who can write a script block into the page, and that’s the main reason to have a policy. Use it as a step on the way.

What is the difference between a nonce and a hash?

A nonce is a random value the server generates for each response and repeats on every script tag it trusts. A hash is the fingerprint of one specific block of code, listed in the policy. Nonces suit pages built on each request. Hashes suit a few static snippets on pages that may be cached.

Why are paths left out of the generated policy?

CSP allows paths such as https://cdn.example.com/lib/, but they’re ignored after a redirect and they break as soon as a file moves. Most deployed policies list origins. If an origin hosts files from many parties, such as a public CDN, pin the exact files with integrity hashes as well.

Can I set a CSP on WordPress or another CMS?

Yes, through the web server configuration or a security plugin that sends headers. The theme and plugins will come with a long list of inline scripts and styles, so begin with the report-only header and the transitional draft, then cut down inline code one plugin at a time.

Where do I see what the policy blocks?

In the Console tab of the browser developer tools. Each violation names the directive and the blocked address. To collect them from visitors, add a report-uri or report-to directive that points to a collector.