Skip to content
Done!
CSP Level 3 · Presets · Report-Only · Multi-Format Output

Content Security Policy Builder

Build a CSP header visually — tick directives, pick sources, and copy the result as a header, Nginx config, Apache rule, meta tag, or Express helmet snippet. Report-only mode is a single toggle, so you can test safely before enforcing.

Last reviewed: August 24, 2026 Built by Khalil Rahman 100% browser-based
Bookmark this page — press Ctrl + D (or ⌘ + D) so your CSP builder is one keystroke away.
Custom directive
No directives selected. Pick a preset above, or tick a directive to start.
Tip: Start in report-only mode. Deploy, watch the browser console for a week, then switch to enforce once nothing legitimate is blocked. The tool shows the correct header name for each mode automatically.

Your policy stays on your device. No directive, source, or custom entry is ever sent to a server. The only network requests are the Bootstrap Icons stylesheet and, if you accept the cookie banner, Google Analytics — which sees page visits, not your CSP configuration.

How to build a CSP

Start with a preset

Pick Strict, Standard, or Permissive — or start from scratch and tick directives one at a time.

Pick sources

Expand a directive to see its common sources. Tick what you need, then add custom domains or nonces with the input at the bottom of each directive.

Choose format and copy

Pick the output format that matches your server or framework. Copy the result, download it, or share it via URL.

What is a Content Security Policy?

A Content Security Policy is a response header that tells the browser which origins are allowed to load scripts, styles, images, fonts, and frames on your page. When a page tries to load something from an origin the policy does not list, the browser blocks it. That is how CSP stops cross-site scripting (XSS) — even if an attacker manages to inject a <script> tag, the browser refuses to execute it because the script came from an origin the policy does not allow.

CSP is one of the few security headers that actually prevents attacks rather than just detecting them. It is the single most effective defence against content-injection bugs, which is why browsers, security scanners, and audit checklists all expect to see one on production sites.

The directives that matter most

A CSP can have dozens of directives, but in practice you only need to set about a dozen to get real protection. The table below explains each one and why it matters.

DirectiveWhat it controlsRecommended starting value
default-srcFallback for any directive you don't set explicitly'self'
script-srcWhere JavaScript can be loaded from'self' + nonces/hashes
style-srcWhere stylesheets and inline styles can come from'self' + nonces if needed
img-srcWhere images can be loaded from'self' data: https:
connect-srcWhere fetch/XHR/WebSocket requests can go'self' + your API origin
font-srcWhere web fonts can be loaded from'self' + your CDN
frame-srcWhere iframes can be embedded from'self' + embed domains
frame-ancestorsWho is allowed to iframe your page'none' unless you're embeddable
base-uriWhere <base> can point to'self'
form-actionWhere forms can submit to'self'
object-srcWhere <object>, <embed>, <applet> can load from'none'
worker-srcWhere Web Workers and Service Workers can be loaded from'self'
manifest-srcWhere the PWA manifest can load from'self'

Common mistakes when writing a CSP

  • Using 'unsafe-inline' in script-src. This allows any inline script to run, which is exactly what CSP is meant to block. If you need inline scripts, use a nonce (a random token generated per request) or a hash of the script contents instead.
  • Forgetting base-uri and form-action. These are not covered by default-src, so leaving them out opens up base-tag hijacking and form-submission redirects. Set both to 'self'.
  • Not listing WebSocket origins. https://api.example.com does not cover wss://api.example.com. If you use WebSockets, list the wss:// origin explicitly in connect-src.
  • Enforcing before testing. A strict CSP that has never been tested in production will almost always block something legitimate — analytics, fonts, hot-reload scripts, error trackers. Run in report-only mode first.
  • Ignoring object-src. Adobe Flash is dead, but <object> and <embed> can still execute plugins. Set object-src 'none' and forget about it.
  • Missing third-party widgets. Chat widgets, review widgets, payment iframes, and embedded videos all need their origins listed in frame-src or script-src. If your support chat disappears after adding CSP, this is why.
  • Setting a CSP but not checking the report endpoint. Report-only mode sends violations to a configured endpoint. If you never look at the reports, you will not know what is broken until you switch to enforce.

Report-only mode — always start here

Content-Security-Policy-Report-Only is the same header with a different name, and the browser treats it completely differently: instead of blocking violations, it allows everything but logs them to the browser console and sends them to your reporting endpoint. This is how you roll out CSP safely.

The standard workflow is:

  1. Build a policy here and toggle Report-only.
  2. Deploy it with a reporting endpoint (a service like report-uri.com, or your own logging endpoint via the report-to directive).
  3. Use the site normally for a week or two. Every block that would have happened shows up in the console or your report stream.
  4. Add the missing origins to your policy — this is the slow, boring part, but it is what makes CSP work.
  5. Once the reports are quiet for a full week, toggle back to Enforce and redeploy.
  6. Keep the reporting endpoint active so you catch any future regressions.

Nonces and hashes — the safe alternative to 'unsafe-inline'

Most single-page frameworks (React, Next.js, Vue, Svelte, Nuxt) inject inline scripts for hydration. This is why developers often reach for 'unsafe-inline' — but that undoes almost all of CSP's protection. The correct approach is one of two things:

  • Nonce. Generate a random string per request, add it to the CSP header as 'nonce-abc123', and add the same value to every <script> tag as nonce="abc123". Next.js and Nuxt have built-in support for this in recent versions.
  • Hash. Compute the SHA-256, SHA-384, or SHA-512 of the exact inline script contents, base64-encode it, and add it to the header as 'sha256-...'. The hash has to match the script byte-for-byte, so this works best for small, stable inline scripts that rarely change.

Both approaches allow your specific inline scripts to run while blocking anything an attacker injects. This tool supports both — just paste the nonce or hash into the custom source field for script-src.

Khalil Rahman

Full-stack developer and founder of SkillsInsider. I've shipped CSPs for SaaS products, e-commerce sites, and internal dashboards — and every single time, the tricky part was never the policy itself, it was tracking down the third-party origins quietly breaking after enforcement. This tool exists because I got tired of writing the same boilerplate header by hand. Send a message if a directive is missing or a source is wrong.

Frequently asked questions

A CSP is an HTTP response header that tells the browser which origins are allowed to load scripts, styles, images, fonts, and other resources on your page. When an attacker injects a script from an unapproved origin, the browser blocks it — that is how CSP stops most XSS attacks. It is one of the few security headers that prevents attacks rather than just detecting them.

Start with default-src 'self', then add script-src, style-src, img-src, connect-src, and font-src. Also set base-uri 'self' and form-action 'self' — those two are not covered by default-src and prevent base-tag hijacking and form-submission redirects, which are often overlooked.

'self' is a keyword that allows resources from the same origin (same protocol, host, and port) as the page. It is the safest starting point — everything else must be explicitly listed. For a page on https://example.com, 'self' allows https://example.com but not https://cdn.example.com, https://api.example.com, or any third-party origin.

In enforce mode, the browser blocks any resource that violates the policy. In report-only mode, the browser allows everything but sends a report to the endpoint you configure. Use report-only first to spot breakage without breaking your users — deploy, watch the console and reports for a week, add the missing origins, then switch to enforce once the reports are clean.

Yes, but most single-page frameworks inject inline scripts for hydration. You usually need either a nonce (generated per request and injected into the script tag) or a hash of the inline script contents. Next.js and Nuxt both support nonce-based CSP out of the box in recent versions. If you set script-src 'self' with no nonce, the framework's own hydration script will be blocked and your app will not load — that is a common gotcha.

'unsafe-inline' allows any inline script or style to run, which is exactly the attack vector CSP is designed to block. If an attacker manages to inject a <script>alert(1)</script> tag, 'unsafe-inline' will let it execute. Use nonces or hashes instead whenever possible. The tool shows a warning banner every time 'unsafe-inline' or 'unsafe-eval' is selected, so you know when your policy has lost most of its value.

A nonce is a random value generated per request and added to the CSP header (as 'nonce-abc123') and to your script tags (nonce="abc123"). A hash is a base64 SHA-256/384/512 of the exact inline script contents, added to the header as 'sha256-...'. Both allow specific inline scripts without opening the door to arbitrary injected ones. Add either one to a directive using the "custom source" input in this tool.

Deploy using Content-Security-Policy-Report-Only (a toggle in this tool) with a reporting endpoint — a service like report-uri.com or your own logging endpoint. Let it run for at least a week, review the reports for legitimate violations, add those sources to the policy, then switch to enforce mode. Deploying a strict CSP cold-turkey to production almost always breaks something, so report-only is not optional — it is part of the workflow.

Check the browser console — every CSP violation is logged with the directive name, the blocked URL, and the policy string. Common causes: a missing CDN origin in script-src or style-src, an iframe source not listed in frame-src, a WebSocket endpoint not in connect-src (remember ws:// and wss:// are separate from https://), and inline event handlers (like onclick="...") being blocked when 'unsafe-inline' is absent. Move inline handlers into external scripts or add them by addEventListener.

No. All directive state is kept in your browser. The only network requests after page load are the Bootstrap Icons stylesheet and — if you accept the cookie banner — Google Analytics. Google Analytics sees the fact that the page was visited but never sees your CSP configuration, since nothing about the policy is ever transmitted. The Share button builds a URL with your configuration in the query string, which you can send to a teammate, but it is only ever copied to your clipboard or shared via your device's share sheet — never uploaded.

More developer tools