Securealy
Check · Security · high · passive

Content-Security-Policy present

A CSP header exists on the HTML response.

Severity
high
Tier
Passive: runs on any URL
Category
Security
Fix
Summary below; full runbook in the Fix Kit, Module 01: Ship it
Id
csp

What this means

Passing means the HTML response for your home page carries a Content-Security-Policy header. Failing means there is none, so the browser will run any script that ends up in the page, from any origin. This check only asks whether the header exists; the next check reads what it says.

How we test it

We fetch the home page over https and read the headers on the final response. A content-security-policy header with any value passes. A content-security-policy-report-only header on its own does not count, because it blocks nothing. No header fails.

Why it matters

It is the only control that limits what injected scripts can do. Every other header on this page narrows one behaviour; CSP is the one that says which origins may run code, load styles, receive form posts and frame the page. It is also the header that breaks more launches than any other, and the breakage is silent: in the kit's own account, a sports data product shipped a strict policy and its subscription button stopped working with no error on the page, because the form's final destination after a redirect was not in form-action. The kit's template is written so that does not happen to you: an allow-list policy with every third party listed, shipped now and tightened later.

The fix

Add the Content-Security-Policy header from the kit's vercel.json template (static) or the csp constant in next-config-headers.ts (Next.js) to the headers block that covers /(.*), then edit the origins so that every script, style, font, image, fetch target and iframe the site actually loads is listed in its directive, and every domain a form can land on after a redirect is in form-action. Push, wait once, confirm with curl -sI https://YOUR_DOMAIN | grep -i '^content-security-policy', then load the site in a browser and fix any console line containing "Refused" or "violates" by adding the named origin to the named directive. The full runbook is in the Fix Kit, Module 01.

Prompt for your AI

Paste this into the coding agent that built the app. It reproduces the finding and stops before changing anything; the fix steps are in the kit.

Do not change anything. This is a verification pass.
Run: curl -sI https://YOUR_DOMAIN | grep -iE '^content-security-policy' and show me the output, or tell me it is empty.
Open vercel.json or next.config at the repo root and show me any Content-Security-Policy entry, or tell me there is none.
Run: grep -rhoE "https?://[a-zA-Z0-9.-]+" --include='*.html' --include='*.js' --include='*.css' --exclude-dir=node_modules . | sort -u
Show me that list of origins; it is what a policy would have to allow.
List which of those origins serve scripts, styles, fonts, images, fetch targets or iframes, and which domains a form on the site could land on after a redirect.
Tell me what it would take to add the header to every route, and stop there.
Run: curl -sI https://YOUR_DOMAIN | grep -ic '^content-security-policy:' and show me the output. Expected on a passing site: 1.

Next

Grade your own URL

Every grade lists this check with its evidence for your site. Two free grades a day.