Seven out of seven does not mean the site is safe
This page counts headers. It reports whether each of the seven arrived and prints the value that came back. It does not grade the value. A site sending Content-Security-Policy: default-src * scores the same as one running a nonce based policy with no unsafe keywords, and the first policy blocks nothing at all.
Treat the number as an inventory, not a grade. The interesting part of any scan is the value text under each header, which is why it is printed in full rather than reduced to a tick.
What the request looks like from the other side
The scan runs from the Toolexe server, not from your browser, because a browser cannot read the response headers of a cross origin site. Your target sees a single HEAD request with a desktop Chrome user agent string, up to five redirects followed, and a ten second ceiling on the whole exchange.
Three consequences worth knowing before you read the output:
- Only public URLs work. Anything behind a login, a VPN, an IP allowlist, or
localhostis unreachable from our side and returns a fetch error rather than an empty result. - Redirects are followed silently. Enter the bare domain on a site redirecting to
wwwand the headers you read belong to the destination. The redirect response itself often carries nothing, which matters because HSTS on a redirect hop is what stops the downgrade in the first place. - HEAD is not GET. Most servers return identical headers for both. Middleware attached to a response body, or a CDN rule matching on content type, sometimes behaves differently. When a header you set is reported missing, confirm with
curl -sIand thencurl -s -o /dev/null -D -before rewriting any config.
The seven, ordered by what to fix first
- 1 Strict-Transport-Security
- Stops the first request from ever going out over plain HTTP after one successful visit. Start at
max-age=300for a day to confirm nothing on the domain still needs HTTP, then raise it tomax-age=31536000; includeSubDomains. Addingpreloadcommits you to a browser shipped list, and getting off that list takes months, so add it last and only if every subdomain including internal ones serves HTTPS. - 2 X-Content-Type-Options
- One value,
nosniff, no tuning, no compatibility risk. It stops the browser from second guessing yourContent-Typeand executing a user uploaded text file as script. If a scan shows this missing, it is the cheapest fix on the list. - 3 X-Frame-Options
SAMEORIGINorDENYkeeps your pages out of an attacker's iframe. The modern replacement is the CSP directiveframe-ancestors, and browsers prefer it when both are present. Keep sending the old header anyway while any traffic comes from clients predating CSP Level 2.- 4 Referrer-Policy
- Without it, browsers apply their own default, and Chrome and Firefox settled on
strict-origin-when-cross-originyears ago. Sending it yourself removes the guesswork and stops full URLs with session tokens or reset links in the query string leaking to third parties through theRefererfield. - 5 Permissions-Policy
- Turns off browser features your site never calls.
geolocation=(), camera=(), microphone=(), payment=()is a reasonable start for a content site. Its real value is containment: an injected third party script cannot prompt for the camera on a page where the feature is switched off at the response level. - 6 Content-Security-Policy
- The one with teeth, and the one with a real chance of breaking your site. Ranked last of the live headers on purpose, because a rushed policy either blocks your own analytics or gets watered down to
unsafe-inlineand stops meaning anything. Roll it out through report only mode first. - 7 X-XSS-Protection
- Retired. Chrome removed the XSS Auditor in 2019, Edge dropped its filter, and Firefox never had one. The only value worth sending today is
0. It still counts toward the score above because scanners across the industry count it, which is a fair criticism of every score of this kind, this one included.
Shipping a CSP without taking the site down
Skip straight to enforcement and you find out about the broken widget from a customer. The order that works:
- Send
Content-Security-Policy-Report-Onlywith the policy you want, plus areport-uriorreport-toendpoint. Nothing is blocked, everything is logged. - Leave it running for a full traffic cycle, at least a week. Marketing tags, payment iframes, and support chat widgets tend to load only on pages nobody tests.
- Read the reports and separate the two kinds of violation: your own resources on a host you forgot to list, and injected junk from a browser extension. Extension noise is heavy and should not shape the policy.
- Replace inline scripts with a nonce or hash. A policy carrying
unsafe-inlinefor scripts gives up most of what CSP was for. - Switch the header name to
Content-Security-Policyand keep the report endpoint live so regressions surface after deploys.
A policy on the paid plan of a reporting service costs less than the outage from step one done wrong.
The header is set and the scan still says missing
This is the most common support question on any header checker, and the cause is nearly always between your app and the visitor rather than in your config file.
- A CDN or proxy rewrote the response. Cloudflare Transform Rules, an AWS CloudFront response headers policy, and Fastly VCL each add and strip headers after your origin is done. Check the edge config, not only the origin.
- The header is set on HTML responses only. nginx
add_headerinside alocationblock for PHP does not apply to the static file block. Test an image URL and an HTML URL and compare. - A nested block wiped the inherited headers. In nginx, any
add_headerin a child block discards everyadd_headerfrom its parent. Repeat them in the child or move them up. This one catches experienced admins. - You tested the redirect, not the page. The scan reports on the final destination after redirects. If the header lives on the redirect response and not the target, it will not show here.
- The response was cached before the change. A shared cache holding the old response serves old headers until it expires or you purge it.
- A meta tag is not a header. CSP set through
<meta http-equiv>does work in the browser but never appears in an HTTP response, so no header scanner will see it. It also cannot carryframe-ancestorsorreport-uri, which is a good reason to move it to the server.
Reading the values, not the ticks
Once the seven come back present, the second pass is where the work is. Things worth catching in the printed values:
- An HSTS
max-ageunder 86400 offers close to nothing. Sites often ship the short test value and never raise it. unsafe-inlineorunsafe-evalinscript-srcremoves most of the protection while keeping the header present.- A CSP with no
object-src 'none'and nobase-uri 'self'leaves two documented bypass routes open. - Duplicate headers, shown here joined with a comma, mean two layers are both setting the value. Browsers apply the strictest CSP of the set, which produces blocking nobody can find in a single config file.
X-XSS-Protection: 1; mode=blockis worse than sending nothing. On the browsers still honouring it, the filter itself introduced information leaks, and that is why0is the current advice.
Where this page stops
No value grading and no letter score. Presence and text only, for the reasons at the top.
No CSP directive parser. The policy is printed as it arrived so you read it yourself. For directive level review, the Google CSP Evaluator is built for exactly that and this page is not trying to replace it.
No cookie flags. Secure, HttpOnly, and SameSite live on Set-Cookie, which a HEAD request often does not trigger, so reporting on them here would be unreliable.
No TLS inspection. Protocol versions, cipher suites, and certificate chains are a different scan. Use the SSL Certificate Checker for the certificate side.
No history. Each run is a fresh request with nothing stored, so there is no trend line and no alert when a deploy drops a header. Wire a curl -sI assertion into your pipeline for that.
