Free tool
What security headers does this site send?
One request, and you see everything the server tells the browser. The HSTS check and the mixed-content check sit on the same page, because between them they answer one question: is this site actually served the way it claims to be?
The mechanism
The server says, the browser does
A security header doesn't protect your server. It's an instruction sent to the browser, which enforces it at the visitor's end. Pull them out one at a time on the left — the column on the right is what happens on their machine.
What the server sends back
HTTP/2 200
Strict-Transport-Security: max-age=31536000; includeSubDomainsContent-Security-Policy: default-src 'self'X-Frame-Options: DENYReferrer-Policy: strict-origin-when-cross-originX-Content-Type-Options: nosniff
5 of 5 headers still sent
What the browser does with it
https://example.com
- An http:// link gets rewritten to https:// by the browser itself, before the request ever leaves.
- A script injected from another domain doesn't run, even though it's sitting right there in the page.
- The page refuses to display inside another site's frame.
- The next site visited gets your domain name, not the full address of the page they left.
- A file announced as text stays text: the browser won't run it as a script.
These instructions only apply in browsers that read them, and only after a first response has arrived: a header never replaces a correct server configuration. The behaviors described are the ones in the specifications (HSTS: RFC 6797).
These headers disappear on the next deployment
You set them once, in a server config or a rules file. Then a host migrates the site, a caching layer goes in front, an extension rewrites responses — and they're gone, without a single page breaking. That's exactly why nobody notices. DomainVigil rereads your sites' headers around the clock and writes to you when one of them drops.
What an automated check cannot judge for you
Whether a header is there is measurable. Whether a content security policy is any good is not. A twelve-directive policy that allows “unsafe-inline” protects less than a three-line policy that doesn't. We flag that one case because it's mechanical and common. The rest has to be read — and tested against the real site, in report-only mode first.
What goes missing
A header drops. Nothing breaks.
A host migration, a cache put in front, an extension rewriting the response: the instruction stops being sent, and the site keeps working exactly the same. That's why nobody notices.
- HSTSstrict-transport-security
- nosniffx-content-type-options
- CSPcontent-security-policy
Reading the table
Seven headers weighed, plus two checks that go with them. A green header is present and correctly set. There's no gray here: a header is either there or it isn't.
- strict-transport-security absent
- Counted as serious. Without it, someone typing your address without “https” starts in the clear, and the redirect that follows can be hijacked before it lands. It's the only header that protects the first request.
- x-content-type-options absent
- Counted as serious, and the easiest to fix: there's only one value, “nosniff”. Without it, a file uploaded through a form can be read as HTML and executed in your visitors' browsers.
- The other five, absent
- Counted as warnings. Each one closes a specific door, and none of them breaks a site when you add it — except the content security policy, which needs testing before you enforce it.
- x-frame-options absent but CSP present
- Shown in green. The “frame-ancestors” directive of the content security policy replaces this header and takes precedence over it in modern browsers. Declaring it as well only helps very old ones.
- The server announces its version
- A warning, not a vulnerability. “nginx/1.18.0” in a response tells anyone looking exactly which published flaws to try. Most servers can hide that line with a single option.
- Mixed content found
- The browser blocks these resources and shows the user nothing. One blocked script, and a form or a menu stops working with no error message. Three examples are listed so you know where to look.
The mistakes that leave a site badly served
Not one of these five throws a visible error. That's what makes them last.
HSTS is declared and http doesn't redirect
What causes it: The two settings live in two different places: the redirect in the server rules, the header in the https response. People configure the second and forget the first.
What fixes it: HSTS is only read after a first encrypted connection. With no redirect, that first connection never happens over https, and the protection never starts. Put the redirect in first.
The content security policy allows “unsafe-inline”
What causes it: The site has scripts written straight into its pages, or a module that injects them. Forbidding inline breaks the page, so it gets allowed to keep the site working.
What fixes it: That throws away this header's main protection. Use a nonce or a hash for the scripts you control, and test in “Content-Security-Policy-Report-Only” before you enforce the policy.
The headers vanish after a cache or a proxy goes in
What causes it: Security headers come from the origin server. A layer in front can replace them with its own, or simply not copy them through.
What fixes it: Check the site from outside, the way this page does, not from the server. The response as seen from outside is the one that counts, and the only one that can lie.
HSTS is set to a short duration “to see if it works”
What causes it: Sensible caution on day one, forgotten after that. The value sits at 300 or 86,400 seconds forever.
What fixes it: A short duration expires between two visits from an occasional user, who comes back in the clear. Raise it to 31,536,000 seconds once the redirect is verified. That's also the minimum for the browsers' preload list.
Adding “preload” without weighing what it means
What causes it: The word gets copied from an example, it's accepted without error, and the submission to the list follows without a second thought.
What fixes it: Browsers refuse a preloaded domain in the clear, every subdomain included, and getting off the list takes months. Check that every subdomain, internal test environments included, is served over https before you ask for it.
The seven headers, their recommended value and what it prevents
The values below come from each header's own specification, or from what the preload program requires. None of them is a house preference.
| Header | Recommended value | What it prevents |
|---|---|---|
| strict-transport-security | max-age=31536000; includeSubDomains | A visitor coming back over an unencrypted connection and having it intercepted. One year is the minimum the browsers' preload list requires. “includeSubDomains” extends the rule to every subdomain, which assumes they're all on https. |
| content-security-policy | default-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self' | An injected script running, resources loading from a site you never authorized, and your pages being displayed inside a frame. Test it in “Content-Security-Policy-Report-Only” before enforcing it. |
| x-content-type-options | nosniff | A file being read as something other than what the server declares. Without it, an uploaded image containing HTML can be executed as a page. There's only one value. |
| x-frame-options | DENY or SAMEORIGIN | Another site putting yours in an invisible frame to hijack your visitors' clicks. Superseded by “frame-ancestors” in the content security policy, which takes precedence over it. |
| referrer-policy | strict-origin-when-cross-origin | The full address of your pages traveling to the next site. A token or an identifier in a URL leaks that way to every third party the page loads. |
| permissions-policy | geolocation=(), camera=(), microphone=() | A third-party script loaded by your page asking for the camera, the microphone or the location. An empty list denies the feature to everyone, your own code included. |
| cross-origin-opener-policy | same-origin | A window opened from your site keeping a usable reference back to yours. It's also the condition for access to high-resolution timers in the browser. |
Three headers are missing from this table and still read by the check: “server”, “x-powered-by” and “x-aspnet-version”. They protect nothing, they announce the software and its version. Hiding them takes one configuration option.
Common questions
Which security headers really matter?
Two, if you only add two: “strict-transport-security”, because it's the only one protecting the first request, and “x-content-type-options: nosniff”, because it costs one line and stops an uploaded file being executed as a page. Then the content security policy, which is the most powerful and the only one that takes real work.
Can these headers break my site?
One of them really can: the content security policy, which blocks whatever it doesn't allow. Test it in “Content-Security-Policy-Report-Only”, which reports without blocking, then switch to enforcing. “strict-transport-security” with “includeSubDomains” deserves care too, because it applies to every subdomain, internal environments included.
Where do I set security headers?
In the web server configuration, which is the most reliable: “add_header” in nginx, “Header always set” in Apache, a headers block in a Caddyfile. Layers in front work too — transform rules at Cloudflare, a “_headers” file on static hosting. Avoid emitting them from application code: a page served from cache never touches the code, and loses its headers.
What is mixed content?
A page served over https that loads a resource over http — an image, a script, a stylesheet. The browser blocks the scripts and stylesheets without warning the user, and shows a degraded padlock. The classic symptom is a feature that stops working with no error message. This check reads the home page and shows up to three examples.
Why is the score out of seven?
Because seven headers are weighed, one point for each one present and correctly set. It isn't a security score: a site with seven perfect headers and a compromised plugin is still vulnerable. It's a count of what's in place, nothing more.
What happens if my site is not reachable over https?
That it couldn't reach it, and nothing else. We don't turn a missing answer into “no security headers” — that would accuse a site we never reached. The http redirect check still runs, and it'll tell you whether the site answers in the clear.
What people assume
Five certainties the check disproves
On the left, what you tell yourself reading your own configuration. On the right, what the response you actually serve shows.
What you tell yourself
What the response shows
HSTS is in place, so my visitors arrive over https.
HSTS is only read after a first encrypted connection. If http doesn't redirect to https, that connection never happens and the protection never starts.
My content security policy has twelve directives, so it's solid.
Twelve directives that allow “unsafe-inline” protect less than three that don't. The injected script runs all the same.
The headers are in the server configuration, so they're served.
A layer put in front — cache, proxy, module — can replace them with its own or fail to copy them through. Only the response seen from outside counts.
x-frame-options is missing, so my pages can be framed.
If your content security policy carries “frame-ancestors”, it replaces that header and takes precedence over it in modern browsers.
Seven headers out of seven: the site is secure.
It's a count of what's in place, not a security score. Seven perfect headers and one breached extension still leave a site vulnerable.
The duration
One year, written in seconds
31536000seconds
The minimum to enter the browsers' preload list.
It's the “max-age” of a one-year HSTS header, and the minimum required to appear on the list browsers preload. Below six months, the protection expires between two visits from an occasional user, who then comes back in the clear. Preloading costs something: browsers refuse a preloaded domain in the clear, every subdomain included, and getting off the list takes months.
Twenty checks, no sign-up
- Has my DNS change propagated yet?
- Build your SPF record
- Is this domain blacklisted?
- A domain's full health report
- When does this domain expire?
- Is this SSL certificate valid, and when does it expire?
- Are your SPF, DKIM and DMARC set up right?
- Is this domain or its mail server blacklisted?
- Which servers receive mail for this domain?
- Which nameservers answer for this domain?
- What do this domain's TXT records do?
- Where does this CNAME end up?