← All Tools

The HSTS max-age=0 Trap — When a Security Header Exists But Provides No Protection

Guide · Last verified Aug 28, 2026

The most common mistake when auditing security headers is stopping at "does the header exist?" For most headers, presence alone is enough. Strict-Transport-Security (HSTS) is the exception — its value can flip its meaning entirely, making it a textbook case where "it's there, so it's safe" turns out to be wrong. This guide covers why that happens and what an automated check needs to look at to catch it.

1. What the HSTS header actually does

HSTS is a response header like Strict-Transport-Security: max-age=31536000; includeSubDomains that tells the browser: "for the next max-age seconds, only ever connect to this domain over HTTPS." If a user accidentally types http://, the browser rewrites it to https:// on its own before the request ever leaves the machine, which shuts down man-in-the-middle downgrade attacks at the source. The max-age value is in seconds, and one year equals 31536000 seconds.

2. What max-age=0 actually means

This is where the trap begins. The HSTS spec, RFC 6797, defines max-age=0 not as "a short validity period" but as an explicit signal to immediately disable the HSTS policy. If a server sends Strict-Transport-Security: max-age=0, the browser stops treating that domain as HTTPS-only from that moment on. The header itself is clearly present in the response, but its actual protective effect is zero. In practice, this value isn't a typo — it's officially defined for cases where you deliberately want to turn HSTS off (for example, when one subdomain hasn't gotten its HTTPS certificate ready yet).

3. Why "the header is there" is a dangerous assumption

If a security scanning script or checklist only greps for the presence of the header, a response with max-age=0 gets misread as "HSTS applied: pass." In reality, that site has zero HTTPS-enforcement protection. This kind of false positive turns into a real incident in situations like these:

4. How to actually check the value

The HTTP Header Checker avoids this problem by parsing HSTS down to its max-age value instead of just checking presence. It extracts the number after max-age= with a regex, and only marks it "Present" if that number is greater than 0 — if max-age=0 or the max-age directive is missing entirely, it's classified as "Missing" regardless of whether the header line technically exists. Other security headers (CSP, X-Frame-Options, etc.) pass purely on presence regardless of their value's specifics; HSTS is the one exception that gets this special-cased logic.

Example: the same "header present" state can be judged completely differently depending on the value.
Actual response headerHeader present?Real protectionCorrect verdict
Strict-Transport-Security: max-age=31536000YesHTTPS enforced for 1 yearApplied
Strict-Transport-Security: max-age=0YesNone (disabled immediately)Same as missing
(no header at all)NoNoneMissing

5. Audit checklist

  1. Check the Strict-Transport-Security value directly with curl -I or your browser's DevTools, and make sure to actually read the number after max-age.
  2. For production, max-age should be at least 6 months (15768000), ideally 1 year (31536000).
  3. Once whatever forced you to temporarily lower HSTS is resolved, add a step to your deployment checklist that restores the original max-age value.
  4. If the header is hardcoded in an Apache/Nginx config, regenerate the standard value (1 year) with the Apache Config Generator or Nginx Config Generator and diff it against what's live.
  5. Cross-check other security headers you might also be missing, such as CSP, with the CSP Generator.

Frequently Asked Questions

Q. Is there ever a legitimate reason to use max-age=0 on purpose?

Yes. It's the officially defined way in RFC 6797 to unwind an HTTPS migration or temporarily allow HTTP access while a subdomain's certificate issue gets sorted out. It should only ever be a temporary measure, though — once the underlying cause is fixed, the original value should be restored immediately.

Q. What about when the header exists but has no max-age value at all?

A Strict-Transport-Security header without a max-age directive is invalid per spec, and browsers ignore it. That case is functionally identical to HSTS not being applied at all.

Q. Do other security headers need value-level checking too?

For most headers like X-Frame-Options or X-Content-Type-Options, presence itself is essentially equivalent to protection. HSTS is unusual in that a single number, max-age, can flip it fully "on" or "off," which is why it specifically needs a separate value check.

Q. What should I watch out for when enabling HSTS for the first time?

If you turn on includeSubDomains, every subdomain needs to be HTTPS-ready. Any subdomain that isn't can become completely unreachable. It's safer to start with a short max-age for testing and gradually increase it.