What is HSTS, and do you need it on your site?

A header checker says "HSTS missing" or "max-age too short". What the header does, how to see if your site sends it, a safe value, and when not to add preload.

· 7 min read

You run a header checker on your site and one line comes back flagged: "Strict-Transport-Security header missing", or "HSTS max-age too short". The site already has a padlock and loads over HTTPS, so it's fair to ask what is left to fix.

The short answer: HTTPS protects a connection once it's open. HSTS makes sure the browser never opens the plain-HTTP one in the first place. It's one line in your hosting config, and the only real risk is a single optional word, preload. Below: what the header does, how to check yours, a safe value for the common hosts, and how to keep it from disappearing.

What HSTS does

When someone types yourdomain.com into the address bar, or clicks an old http:// link, the browser's first request goes out over plain HTTP. Your server answers with a redirect to https://, and from there everything is encrypted. That first request is not. On public Wi-Fi, or any network someone else controls, it can be intercepted: the attacker answers instead of your server and keeps the visitor on an HTTP copy of your site. The padlock never appears, and most people don't notice.

Strict-Transport-Security closes that gap. It's a response header, defined in RFC 6797, that tells the browser: "for the next N seconds, only ever talk to this domain over HTTPS." After the browser has seen it once, it rewrites every http:// request to your domain into https:// before anything leaves the device. It also stops visitors from clicking through a certificate warning on your domain.

The header has three parts:

  • max-age: how long, in seconds, the browser should remember the rule. 31536000 is one year. Each visit that sees the header starts the clock again.
  • includeSubDomains: apply the same rule to every subdomain, such as app.yourdomain.com and www.yourdomain.com.
  • preload: a request to be added to the list of HTTPS-only domains built into browsers. More on this below, because it's the part that can hurt.

Two details from MDN's reference explain most of the confusion around it. The browser ignores the header if it arrives over plain HTTP, so sending it from your HTTP redirect does nothing. And the very first visit is still unprotected, because the browser hasn't seen the header yet. That's what preloading is for.

Why AI-built sites are flagged for it

Whether your site sends HSTS depends almost entirely on the host, not on the code your AI tool wrote. Lovable, Bolt, v0 and Cursor generate pages and components; none of them adds response headers unless you ask.

  • Vercel adds Strict-Transport-Security to every HTTPS response by default, with a two-year max-age (see Vercel's notes).
  • Netlify sites often return it, but the value isn't promised in the docs, so check your own site.
  • Your own server (a VPS with nginx or Caddy, a Docker container behind a proxy) sends exactly what you configured. A fresh nginx sends no HSTS at all.
  • A CDN or proxy in front of the host (Cloudflare, for example) may add, replace or strip the header, depending on its settings.

The "too short" warning usually comes from a value someone copied from a tutorial: max-age=300 or max-age=86400, meant for testing and never raised. A one-day rule protects only visitors who came back within the last day.

Check it yourself

1. See what your site sends

curl -sI https://yourdomain.com | grep -i strict-transport

Possible results:

  • Nothing printed: no HSTS.
  • strict-transport-security: max-age=63072000 or similar: present. Divide by 86400 to get days. Anything under 180 days is short; one year or more is the usual value.
  • Two lines: the host and your config both add it, and browsers use only the first one. Remove one of them.

Check the www version and one deep page too (https://yourdomain.com/pricing). If the bare domain redirects to www, the redirect response itself should also carry the header, otherwise visitors who type the bare domain never get the rule for it.

No terminal? Open the site in Chrome, open DevTools (F12), go to Network, reload, click the first request and look for strict-transport-security under Response Headers.

2. Check that HTTP actually redirects

curl -sI http://yourdomain.com | head -3

You want a 301 or 308 and a location: https://... line. If HTTP serves the page with a 200 instead, fix that first: HSTS only helps visitors who have reached the HTTPS version at least once.

3. List your subdomains before you add includeSubDomains

Write down every subdomain that resolves: www, app, api, staging, mail, an old blog on another platform. Open each with https://. If any of them has no valid certificate, includeSubDomains will lock visitors out of it for the whole max-age, because browsers won't offer a "continue anyway" button on an HSTS domain.

Fix it: a safe value for your host

Start with this:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Leave out includeSubDomains if step 3 found a subdomain without working HTTPS. Leave out preload for now.

Where to put it:

  • Vercel: nothing to do unless you want a different value. If you do, add the header to the headers section of vercel.json or next.config.
  • Netlify: add it under /* in a _headers file in your publish folder, or in netlify.toml. The full file is in How to add security headers on Netlify.
  • nginx: inside the server block that listens on 443, add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;. Note that nginx drops inherited add_header lines in any location block that defines its own headers, so check a deep page after the change.
  • Caddy: header Strict-Transport-Security "max-age=31536000; includeSubDomains" in the site block.

If you're nervous, roll it out in steps: max-age=300 for a day, then a week, then a year. If something breaks, a short max-age means it fixes itself quickly. To turn HSTS off, send max-age=0 over HTTPS; browsers forget the rule on their next visit.

Prompt for Lovable, Cursor or Claude Code:

Add an HSTS header to this project's HTTPS responses.

1. Find where response headers are set (vercel.json, next.config,
   netlify.toml, public/_headers, nginx or Caddy config). Tell me which
   one is used in production and whether the host already sends
   Strict-Transport-Security.
2. If it does not, add:
   Strict-Transport-Security: max-age=31536000; includeSubDomains
   for all paths. Do not send it on plain HTTP responses.
3. Make sure http:// redirects to https:// with a 301 or 308.
4. Do NOT add the preload directive.
5. If the header is set in more than one place, keep one.
6. Show me the curl commands to check the header on the home page,
   the www version and one deep page after deploy.

About preload: the part that's hard to undo

The HSTS preload list is a list of domains compiled into Chrome and used by the other major browsers. A preloaded domain is HTTPS-only from the very first visit, which closes the gap HSTS can't. To be accepted, the site needs a valid certificate, an HTTP-to-HTTPS redirect, and a header with max-age of at least one year, includeSubDomains and preload.

The catch is that getting off the list takes months: removal has to ship in new browser versions, and people on old versions keep the rule. Any subdomain that later needs plain HTTP, such as an old service or a third-party tool on a CNAME, simply stops working for them. For most small sites the normal header is enough. Preload makes sense when you control every subdomain and expect to keep HTTPS on all of them for years.

If you add the word preload to the header without submitting the domain, nothing happens. Don't add it "just in case": someone else, or a tool, can submit any domain whose header asks for it.

Keep it from disappearing

HSTS rarely breaks a site. It disappears quietly instead. A move from Netlify to a VPS, a new proxy in front of the app, or an assistant asked to "simplify the config" drops the header, and the site keeps working. Visitors who already have the rule stay protected until it expires; new visitors never get it. Check the header after any change to hosting, domains or headers config, not just once.

That's one of the checks ScanYourSite runs on your live site from the outside. The free check reports missing HSTS, a max-age under 180 days, and a site that serves plain HTTP without redirecting to HTTPS, along with the other security headers and cookie flags, and gives you a prompt to fix each one. The free plan checks one site once a week, sends a weekly summary and warns you before your certificate expires. Pro checks daily and after every deploy, and alerts you right away when a header goes missing.

Try it on your domain: https://scanyoursite.app/?check=yourdomain.com.

Check your own site in a minute

Free, no sign-up. You get a prioritised list of what’s wrong and a prompt you can paste back into Lovable, Bolt, Cursor or Claude Code.

Free, no sign-up. The basic check makes only the requests an ordinary browser would.

Security headers