Security Headers Checker
Check HSTS, CSP, X-Frame-Options and cookie flags, with copy-paste fixes for common servers.
About the security headers checker
HTTP security headers are response headers that tell the browser how to treat a page defensively, closing off entire classes of attack without changing the page's content. This tool requests the page and evaluates the headers it actually returns, since a header that exists in a config file but is not on the live response provides no protection at all.
Strict-Transport-Security (HSTS) and Content-Security-Policy (CSP) generally have the largest impact. HSTS instructs the browser to only ever connect to the site over HTTPS for a set duration, preventing a downgrade to plain HTTP even if a user types the address without https://. CSP restricts which sources scripts, styles and other resources may load from, which is one of the more effective defenses against cross-site scripting.
X-Content-Type-Options, X-Frame-Options (or the frame-ancestors directive in CSP, which supersedes it) and Referrer-Policy round out the common set: they stop the browser from guessing a file's type in a way an attacker can exploit, prevent the page from being embedded in a hostile iframe for clickjacking, and control how much of the current URL leaks to sites you link to. Referrer-Policy is worth setting explicitly to strict-origin-when-cross-origin, because several of the values still in circulation leak the full URL, including its query string, to every site you link to.
Cookies are graded from the Set-Cookie headers the response actually sends. Secure keeps a cookie off plain HTTP, HttpOnly keeps it out of reach of page scripts, and SameSite limits when it is sent on cross-site requests. A cookie marked SameSite=None without Secure is not merely weak — browsers have rejected that combination outright since Chrome 80, so the cookie is not set at all. The __Host- and __Secure- name prefixes are also checked, because a cookie claiming a prefix without meeting its conditions is discarded by the browser rather than honoured.
CORS is checked separately, and it is the one most likely to surface something you did not know was there. The check sends two different invented origins and compares the responses against a request with no Origin header at all. A server that echoes back whatever origin it was sent is reflecting rather than allow-listing, which means any site on the internet can read the response; combined with Access-Control-Allow-Credentials, that is a direct path to reading an authenticated user's data from another origin, and it is graded accordingly. A static allow-list is never flagged. Where the allow-origin header varies by request, the response also needs Vary: Origin, or a shared cache will hand one origin's response to another.
Common questions
- Which headers matter most?
- Strict-Transport-Security and Content-Security-Policy have the biggest practical impact, followed by X-Content-Type-Options, X-Frame-Options (or frame-ancestors) and Referrer-Policy.
- Will a CSP break my site?
- It can, if the policy is stricter than what the page actually loads. Start with Content-Security-Policy-Report-Only, review the violations it logs, and only switch to enforcing once the policy is accurate.
- What about cookies?
- Session cookies should be marked Secure, HttpOnly, and SameSite=Lax or Strict, which limits both network eavesdropping and script-based theft of the cookie.
- Does the header need to be exactly right, or is close enough fine?
- It needs to be exact. A typo in a directive name, or a missing quote around a CSP source, is silently ignored by the browser rather than causing an error, which can leave a policy far weaker than intended.
- Is a permissive CORS policy actually dangerous?
- It depends on credentials. Reflecting any origin back on a public, unauthenticated endpoint is untidy; doing it alongside Access-Control-Allow-Credentials lets any site read responses as your logged-in user, which is why we grade the two cases very differently.
- Why does my Access-Control-Allow-Origin with several domains not work?
- The header takes exactly one origin, or the single wildcard. A comma-separated list is not valid. The correct implementation compares the request's Origin against your allow-list and echoes back only that one value, adding Vary: Origin.
- Why is report-only CSP graded as no CSP?
- Because it enforces nothing. Content-Security-Policy-Report-Only is the right way to develop a policy and it blocks nothing while it is the only policy present, so a site running report-only has the same protection as a site with no policy at all.
What this tool checks (41 rules)
- web.security-headers.coep-missing — No Cross-Origin-Embedder-Policy is set
- web.security-headers.coop-missing — No Cross-Origin-Opener-Policy is set
- web.security-headers.corp-missing — No Cross-Origin-Resource-Policy is set
- web.security-headers.csp-missing — No Content Security Policy is enforced
- web.security-headers.csp-no-object-or-base — The policy leaves plugins or the base URL unrestricted
- web.security-headers.csp-report-only — The Content Security Policy is report-only
- web.security-headers.csp-unsafe-eval — The policy allows eval()
- web.security-headers.csp-unsafe-inline — The policy allows inline scripts or styles
- web.security-headers.csp-wildcard-source — The policy allows resources from anywhere
- web.security-headers.duplicate-conflicting — A security header was sent twice with different values
- web.security-headers.hsts-max-age-too-low — HSTS max-age is shorter than six months
- web.security-headers.hsts-missing — HTTP Strict Transport Security is not in force
- web.security-headers.hsts-no-subdomains — HSTS does not cover subdomains
- web.security-headers.hsts-not-preloaded — The site is not set up for HSTS preloading
- web.security-headers.malformed-value — A response header is malformed
- web.security-headers.no-frame-protection — The page can be embedded in another site
- web.security-headers.permissions-policy-missing — No Permissions-Policy is set
- web.security-headers.referrer-policy-missing — No Referrer-Policy is set
- web.security-headers.referrer-policy-weak — The Referrer-Policy leaks full URLs
- web.security-headers.server-version-disclosed — The server announces its exact version
- web.security-headers.technology-disclosed — Response headers name the application stack
- web.security-headers.xcto-missing — Browsers may guess the type of your responses
- web.cookies.domain-not-accepted — Cookie sets a Domain the browser will not accept, so it is discarded
- web.cookies.malformed-attribute — Cookie carries an attribute browsers cannot read
- web.cookies.no-httponly — Cookie can be read by JavaScript
- web.cookies.no-samesite — Cookie has no SameSite attribute
- web.cookies.no-secure — Cookie is sent over unencrypted connections
- web.cookies.none-set — The page set no cookies
- web.cookies.overly-broad-domain — Cookie is shared with every subdomain
- web.cookies.prefix-requirements-unmet — Cookie name promises protections its attributes do not provide
- web.cookies.samesite-none-insecure — Cookie uses SameSite=None without Secure, so it is rejected
- web.cors.headers-overly-broad — The CORS policy accepts any request header from any origin
- web.cors.malformed-allow-origin — The Access-Control-Allow-Origin value is not a valid origin
- web.cors.methods-overly-broad — The CORS policy grants broad methods on an unrestricted origin
- web.cors.null-origin-allowed — The null origin is on the CORS allow-list
- web.cors.origin-reflected — Any website is allowed to read this site's responses
- web.cors.origin-reflected-with-credentials — Any website can read authenticated responses from this site
- web.cors.preflight-not-cached — Preflight results are not cached
- web.cors.vary-origin-missing — The CORS response varies by origin but is not marked Vary: Origin
- web.cors.wildcard-origin — Resources are shared with every origin, without credentials
- web.cors.wildcard-with-credentials — The CORS policy pairs a wildcard origin with credentials