Text & Data

CSP Checker

Paste a Content-Security-Policy and an HTML page to see what the policy blocks (inline scripts, styles, event handlers, external resources) and the exact hashes that would allow the inline parts. Nothing is fetched. Runs in your browser.

Tool request · Agent

Content-Security-Policy checker (policy + HTML → what would be blocked, and the hashes to allow it)

Asked for on October 15, 2026. Who asked is never published. Request a tool

CSP checker options

Used for 'self' and to resolve relative URLs. Without it, relative URLs count as same-origin and absolute ones as cross-origin.

Keyboard shortcuts

Esc
Close a menu or overlay, then leave full screen
↓ on a menu
Open it; ↑ ↓ move through its items
← → in the menu bar
Move between menus

Where your data goes

Using this pageUsing the API, MCP or A2A
Where it runsIn your browser. The policy and the HTML are parsed and checked by JavaScript on this page; nothing is sent anywhere, and none of the URLs in the HTML are fetched.In a Cloudflare Pages Function at the Cloudflare data centre nearest the caller.
What leaves your deviceNothing you enter. After the page and its scripts load, no request carries your input. The browser enforces this: the page's Content-Security-Policy sets connect-src 'none' and form-action 'none', which block fetch, XHR, WebSocket, EventSource, beacons and form posts.

The server receives the policy and the HTML, analyses them in memory inside the request, and returns the result. Nothing is fetched: the server makes no network requests, not even for the URLs in the HTML. Your input is not stored or logged; only the per-call metadata row (operation, time, status, sizes) is recorded. For unpublished pages use the page, which does the same work in your browser and sends nothing.

StoredNothing leaves the page. No cookies, analytics or third-party scripts; the browser keeps only your View → Inline config choice.Your inputs and results: never. What is stored is your client record (client ID, a SHA-256 hash of your secret, expiry, and the profile you registered with) and one metadata row per call: operation, time, transport, status, error code, duration, request and response sizes, and country.
LoggedNothing (no requests are made).Only the per-call metadata above, attributed to your client ID. Never parameters, bodies, results, or credentials. Cloudflare, as the host, also processes request metadata (IP address, full URL including the query string, headers) under its own policies, so put inputs in POST bodies.
AccessOpen to anyone.Requires a client ID and secret (how to get one). HTTPS only; TLS terminates at Cloudflare.

Details and how to verify this yourself: Security & privacy.

User guide

The tool sits in a window that works like a desktop app: menus on the top row and the full-screen icon at its right (Esc leaves full screen). The page URL option described below is in Settings → Options…, an overlay. To keep it in view instead, choose View → Inline config; the page remembers that choice. Help → Keyboard shortcuts… lists the keys.

  1. Paste the policy on the left: the value of your Content-Security-Policy header, for example default-src 'self'; script-src 'self'. Leave it empty if the HTML carries its own <meta http-equiv="Content-Security-Policy">.
  2. Paste the HTML on the right: the page as your server sends it (view source, or a file from your build output), not what the browser’s inspector shows after scripts have run. Sample loads a page with something blocked of each kind.
  3. Read the verdict and the Findings: every inline script, <style>, style="" attribute, event handler (onclick=…), javascript: link and external resource, blocked ones first. Each says which directive applied, and either the source that allowed it or why it is blocked and how to fix it.
  4. Hashes to add lists, per directive, the exact 'sha256-…' sources that would allow everything inline that is blocked, ready to copy into your policy. Attributes also need 'unsafe-hashes', which is included when it applies.
  5. Under each Policy you see its directives, which directive governs each kind of resource after fallback, and warnings about weaknesses ('unsafe-inline', wildcards, no object-src 'none', no base-uri, sources missing their quotes).

With only a policy, the page is a policy linter: it parses it, resolves the fallbacks and lists its warnings.

Page URL (Settings). 'self' means “the page’s own origin”, so give the URL the page is served from to check absolute URLs against 'self' correctly. Without it, relative URLs count as same-origin and absolute ones as cross-origin.

For agents. csp_check over the API, MCP or A2A takes policy, html and url and returns the same findings. A good use: after building a site with a strict policy, check each built page before deploying.

Specification

The checker implements the parts of Content Security Policy Level 3 that can be decided from the policy and the HTML alone.

Parsing. A header value may hold several policies separated by commas; each is enforced, so something is allowed only if every policy allows it (CSP3 §4.1). Within a policy, directives are separated by ;, names are case-insensitive, and a repeated directive is ignored after its first occurrence (§2.2.1). A <meta http-equiv="Content-Security-Policy"> policy is enforced too, but only for the elements after it, and it cannot set frame-ancestors, report-uri or sandbox (§3.3).

Fallbacks (§6.8.3). Each request or inline item has an effective directive; the first of its fallback list that the policy contains applies, and if none does it is unrestricted:

Effective directiveFalls back to
script-src-elemscript-src, default-src
script-src-attrscript-src, default-src
style-src-elemstyle-src, default-src
style-src-attrstyle-src, default-src
worker-srcchild-src, script-src, default-src
frame-srcchild-src, default-src
img-src, font-src, media-src, connect-src, manifest-src, object-srcdefault-src
base-uri, form-action, frame-ancestorsnothing: unrestricted unless set

What is checked, and against what.

FindingFromDirective
inline-script<script> without src whose type is JavaScript, module, importmap or speculationrulesscript-src-elem
inline-style<style>style-src-elem
style-attributeany style="" attributestyle-src-attr
event-handlerany on…="" attributescript-src-attr
javascript-urljavascript: in a/area href, iframe/frame src, form action, formactionscript-src-elem
external-script<script src>, <link rel=modulepreload>, <link rel=preload as=script>script-src-elem (worker-src for as=worker)
external-style<link rel=stylesheet>, <link rel=preload as=style>style-src-elem
image<img src> and each srcset URL, <source srcset>, <input type=image>, <video poster>, <link rel=icon>img-src
font, connect, media, manifest<link rel=preload as=font / fetch / audio / video / track>, <audio>, <video>, <source src>, <track>, <link rel=manifest>font-src, connect-src, media-src, manifest-src
frame, object<iframe src>, <frame src>; <object data>, <embed src>frame-src; object-src
form-action, base-uri<form action>, formaction; <base href>form-action; base-uri

<script> elements with any other type (application/json, application/ld+json, text/template, …) are data blocks: browsers never run them and CSP never checks them (HTML “prepare the script element”). They are counted, not reported.

Inline content (§6.7.3.3). In order:

  1. 'unsafe-inline' allows it, unless the same list has a hash or a nonce, or (for scripts) 'strict-dynamic'; then 'unsafe-inline' is ignored.
  2. For <script> and <style> elements, a nonce attribute equal to a 'nonce-…' source allows it, if the element is nonceable (no attribute name or value contains <script or <style, the HTML spec’s dangling-markup check).
  3. A 'sha256-…', 'sha384-…' or 'sha512-…' source equal to the hash of the content allows it. For style and event-handler attributes this applies only when the list also has 'unsafe-hashes'. Hash values may be base64 or base64url.

javascript: URLs are allowed only by 'unsafe-inline' here; hash-allowing them (with 'unsafe-hashes') is not evaluated.

Hashes are computed as browsers compute them: over the UTF-8 bytes of the element’s text exactly as written between the tags, after the HTML parser’s newline normalization (CR LF and lone CR become LF), and, for attributes, over the value after character references are decoded (&quot; becomes "). The hash field is always sha256; any of the three algorithms in the policy is matched.

External resources (§6.7.2). A nonce on <script> or <link rel=stylesheet> matching a 'nonce-…' source allows the request. A script whose integrity hashes are all listed as hash sources is allowed. With 'strict-dynamic', host, scheme and 'self' sources are ignored for scripts, so a script in the HTML needs a nonce or integrity hash; scripts that those scripts load later are trusted but cannot be seen here. Otherwise the URL is matched against each source:

  • * matches http and https URLs and the page’s own scheme, but not data:, blob: or filesystem:.
  • 'self' matches the page’s origin, and the same host over https or wss from an http page.
  • A scheme source such as https: matches that scheme; http: also matches https:, ws: matches wss:, http: and https:.
  • A host source matches its host (*.example.com matches subdomains, not example.com itself), its scheme if given (with the same upgrades), its port (or the scheme’s default port, with 80 upgrading to 443; :* matches any port), and its path: a path ending in / matches everything under it, any other path matches exactly.
  • 'none' matches nothing; listed with other sources it is ignored.

Relative URLs resolve against the page URL, or against a <base href> that base-uri allows.

Not evaluated. Nothing is fetched, so redirects, CSS @import and url(), workers created by scripts, fetch() calls, and anything scripts add to the page at run time are not seen. frame-ancestors, sandbox, upgrade-insecure-requests, trusted-types and reporting directives are parsed and listed but have nothing to check in a document. The tokenizer reads the document as the HTML parser does for the cases CSP depends on (comments, raw text in <script>, <style>, <textarea> and <title>, attribute quoting, the first of duplicate attributes), but it does not build a tree, so it cannot tell <source> in <picture> from <source> in <video> (srcset is treated as images, src as media) and does not handle the rare <!--<script> escape inside scripts.

Limits. API: policy at most 20,000 characters and html at most 500,000. The page has no limit.

API. POST /api/csp/check with policy, html and url. Something blocked is a 200 response; only a missing policy or an invalid url is an error. With format=text, the response is allowed, or blocked: <n> followed by one line per blocked finding.

API & MCP

The same logic is available over HTTP and as MCP tools. Unlike this page, these run on the server: see where your data goes.