---
title: Why isn't my site HSTS preload-ready? How to check with the HSTS Checker
description: You added Strict-Transport-Security, but the preload list refuses the domain, or a scanner still says the first visit isn't protected. To check HSTS, read the header on the bare domain's own HTTPS response, without following its redirect, and compare it with the preload rules. Here's how to do it with the free HSTS Checker, how to read max-age, includeSubDomains and preload, and the exact header the list expects.
canonical: https://watchfor.io/blog/how-to-check-hsts
---

# Why isn't my site HSTS preload-ready? How to check with the HSTS Checker

2026-10-04 · WatchFor Team · Guides · guides, security, ssl

You added Strict-Transport-Security, but the preload list refuses the domain, or a scanner still says the first visit isn't protected. To check HSTS, read the header on the bare domain's own HTTPS response, without following its redirect, and compare it with the preload rules. Here's how to do it with the free HSTS Checker, how to read max-age, includeSubDomains and preload, and the exact header the list expects.

To check HSTS, request the site over HTTPS and read the
`Strict-Transport-Security` header: `max-age` says how many seconds browsers
must stick to HTTPS, `includeSubDomains` extends that to every subdomain,
and `preload` asks to be built into browsers. For the preload list, check
the bare domain (`https://example.com`) **without following its redirect**:
the list reads the header on that first response, not on the page it
redirects to.

The free [HSTS Checker](/hsts-checker) fetches the URL from a WatchFor probe,
parses the header and says whether it meets the preload requirements, with
a line per directive. No signup.

## What this tool answers

*Does this host send HSTS, for how long, does it cover subdomains, and would
the browser preload list accept it?* Reach for it when a security scan says
the first visit can be downgraded to HTTP, when hstspreload.org rejects your
submission, after moving a site behind a new CDN or load balancer, or before
you add `includeSubDomains` and want to know what's already in place. Your
own browser can't tell you: once it has seen the header, it upgrades to
HTTPS silently.

## A real example

### Checking HSTS on the bare domain, where the preload list looks

The usual situation: someone added HSTS to the website, the homepage shows
the header, and the preload submission still fails. The header is on `www`,
but the list checks the bare domain. We looked at how a well-known site
answers on both names.

In the [HSTS Checker](/hsts-checker) we entered `https://mozilla.org`
(the bare domain), **unticked Follow redirects** so we'd see the domain's
own response rather than the page it redirects to, and pressed **Fetch
headers**. The run came from the Vilnius probe:

![The HSTS Checker for https://mozilla.org with Follow redirects unticked: status 301, TTFB 716 ms, and an amber Strict-Transport-Security card reading max-age=60; includeSubDomains, marked "Present, not preload-ready", with the verdicts: max-age is 0 days, browsers accept it but the preload list needs at least 1 year; includeSubDomains covers every subdomain; no preload directive](/blog/how-to-check-hsts-result.webp)

The bare domain answered **301** to `https://www.mozilla.org/` and sent:

```text
Strict-Transport-Security: max-age=60; includeSubDomains
```

The checker marked it **present, not preload-ready**:

- **max-age is 60 seconds.** A browser keeps the HTTPS-only rule for one
  minute after the visit.
- **includeSubDomains** is set (green): for that minute, every subdomain is
  HTTPS-only too.
- **No preload directive**, so the domain can't be submitted.

`www.mozilla.org` sends its own header, `max-age=31536000` (a year) with
no `includeSubDomains` (see the command line below). And `mozilla.org`
isn't on the preload list: hstspreload.org reports its status as
`unknown`.

What that means in practice: someone typing `mozilla.org` for the first
time, or more than a minute after their last visit, makes a plain HTTP
request first (`http://mozilla.org/` answers with a 302 to HTTPS). That
first request is the one HSTS exists to protect. Whether that's a
deliberate choice we can't tell from outside; there are good reasons to
hold back, such as subdomains that can't serve HTTPS yet.

**The answer:** for your own domain, preload needs all of this on the bare
domain, taken from [hstspreload.org](https://hstspreload.org/):

1. A valid certificate, and every subdomain (including `www`) served over
   HTTPS.
2. `http://example.com` redirects to `https://example.com` (same host)
   before going anywhere else.
3. The HTTPS response on the bare domain, **including a redirect**, sends
   `max-age` of at least `31536000`, `includeSubDomains` and `preload`.

In nginx, on the server block that answers `https://example.com`, `always`
included so error responses carry it too:

```nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
```

Ramp up rather than jumping straight to two years: a few minutes, then a
week, then a month, checking that nothing breaks, before you submit.

## How to read the results

| What you see | What it means |
| --- | --- |
| **Status** | The status of the response that was checked. With Follow redirects off, a 301 is expected on a bare domain that redirects to `www`. |
| **Strict-Transport-Security card** | The raw header and a verdict: green **Preload-ready**, amber **Present, not preload-ready**, red **Missing**. |
| **max-age line** | The policy length in days. Green from one year, amber under a year, amber for `max-age=0` (which deletes the policy), red when missing or unreadable. |
| **includeSubDomains line** | Green when every subdomain is covered. Amber when not; a subdomain reachable over HTTP stays open, and the list requires it. |
| **preload line** | Green when present. Amber when absent: the first visit ever stays unprotected. |
| **Fetched over http://** | Browsers ignore HSTS on plain HTTP. If you checked an `http://` URL, check the `https://` one. |
| **Security headers and header list** | The other common security headers and every response header, as on the [HTTP header checker](/http-header-checker). |

## More ways to use it

- **Before a preload submission.** Run the bare domain with Follow redirects
  off, then `www` and one or two subdomains.
- **After a CDN change.** Many CDNs add or strip HSTS at the edge. Confirm
  the header and max-age didn't change.
- **Rolling HSTS back.** Send `max-age=0` and check it's live on every host
  before removing the header; browsers forget the policy only when they see
  `max-age=0` or the old max-age runs out.
- **Testing every subdomain before includeSubDomains.** Run each one you
  know of; any that can't do HTTPS would break once the policy covers it.

## Tips and gotchas

- **Your browser remembers.** After one HTTPS visit, the browser upgrades to
  HTTPS on its own until max-age ends, so testing in a browser shows nothing.
  The checker starts fresh every time.
- **Preloaded doesn't always mean "has the header".** Whole TLDs are
  preloaded: hstspreload.org reports `playwright.dev` as preloaded through
  `dev`, so every `.dev` domain is HTTPS-only in browsers whatever it sends.
  Domains on the list are expected to keep the `preload` directive;
  removing it makes them eligible for removal.
- **Preload is hard to undo.** Removal takes months to reach users through
  browser updates. Be sure every subdomain can do HTTPS, for good.
- **HSTS on the redirect counts.** If `https://example.com` redirects to
  `www`, the header must be on that 301 itself, as the list requires.
- **It doesn't fix HTTP links.** HSTS upgrades requests the browser makes;
  it doesn't change `http://` URLs inside your pages. More background in
  [HSTS explained](/blog/hsts-explained).

## Check it from the command line too

`curl -sI` doesn't follow redirects unless you add `-L`, so it shows the
bare domain's own header:

```bash
curl -sI https://mozilla.org/ | grep -iE '^(HTTP|location|strict-transport)'
curl -sI https://www.mozilla.org/ | grep -iE '^(HTTP|strict-transport)'
```

```text
HTTP/2 301
location: https://www.mozilla.org/
strict-transport-security: max-age=60; includeSubDomains
HTTP/2 200
strict-transport-security: max-age=31536000
```

The plain-HTTP first request that HSTS is meant to replace:

```bash
curl -sI http://mozilla.org/ | grep -iE '^(HTTP|location)'
```

```text
HTTP/1.1 302 Found
Location: https://mozilla.org:443/
```

And the preload list's own view, from its public API:

```bash
curl -s "https://hstspreload.org/api/v2/status?domain=mozilla.org" | jq -c '{name,status}'
```

```text
{"name":"mozilla.org","status":"unknown"}
```

## Keep watching it

HSTS is easy to lose without noticing: a CDN rule, a new `location` block,
a migration that drops a header. A
[page integrity monitor](/page-integrity-monitoring) keeps each watched
page's security headers in its baseline and raises a critical **Security
header weakened** finding when HSTS is removed or its max-age gets shorter.
For the redirect hop itself, watch the bare domain's URL.

## FAQ

**How do I check if HSTS is enabled?**
Request the site over HTTPS and look for the `Strict-Transport-Security`
response header, with the HSTS Checker, with `curl -sI https://example.com`,
or in the browser's developer tools under the document's response headers.

**What max-age should HSTS have?**
At least `31536000` (one year) for the preload list; two years
(`63072000`) is common. Start with a few minutes while you test, then raise
it in steps.

**Why does hstspreload.org reject my domain?**
Usually because the header is only on `www`, not on the bare domain's HTTPS
response (including its redirect), or because `includeSubDomains`, `preload`
or a one-year `max-age` is missing. Check the bare domain with redirects not
followed. For the other headers worth sending, see the
[security headers guide](/blog/how-to-check-security-headers).

---

Canonical page: https://watchfor.io/blog/how-to-check-hsts · All articles: https://watchfor.io/blog/all.md · Site guide: https://watchfor.io/llms.txt
