How to Fix Missing HSTS Header (Step-by-Step)

Fix a missing HSTS header on Cloudflare, Nginx, Apache and more. Here is what Strict-Transport-Security does, why it matters and how to verify the fix.

hstshttp-headerswebsite-healthguide

If you have scanned your website and received a warning about a missing HSTS header, you are not alone. A missing HSTS header is one of the most common findings and one of the quickest to fix. This guide walks you through what HSTS is, why your site needs it and how to enable it on every major platform.

What is covered

What is HSTS and why does it matter?

HSTS is the Strict-Transport-Security response header. It tells browsers one thing: “Only connect to this site over HTTPS. Never use HTTP.”

Without HSTS, here is what can happen. A user types yoursite.com into their browser. The browser first tries http://yoursite.com. Your server redirects to https://yoursite.com. That initial HTTP request - before the redirect - travels in the clear. Anyone else on the same network (coffee shop Wi-Fi, hotel network, airport) can read that first request, copy cookies, inject content or send the user to a look-alike version of your site. That is what people mean by a protocol downgrade.

HSTS closes that first-request gap. Once a browser sees the HSTS header, it remembers: “This site is HTTPS-only.” Every future visit goes straight to HTTPS - no HTTP request, no redirect, no gap.

This is not a theoretical risk. SSL stripping is well documented and the tooling for it is freely available. If your site handles any user data - logins, forms, personal information, payments - treat HSTS as essential.

What changes once HSTS is on

HSTS does three specific things:

No protocol downgrade. Browsers refuse to connect over HTTP, even if the user types http:// explicitly or clicks an old HTTP link. The browser automatically upgrades to HTTPS before making the request.

No mixed content fallback. If any resource on your page tries to load over HTTP, the browser blocks or upgrades it. Nothing on the page can quietly fall back to plain HTTP.

Cookies stay on HTTPS. Without HSTS, a cookie set without the HTTPS-only cookie flag can leak over a plain HTTP connection. HSTS keeps the connection encrypted on every visit, which helps even when your cookie flags are not perfect.

Before you enable HSTS

HSTS is powerful, but it is also a commitment. Once a browser caches your HSTS policy, it will refuse to connect over HTTP until the max-age expires. If your HTTPS setup has problems, users will not see a warning - they will see a hard error with no way to bypass it.

Before enabling HSTS, verify these things:

Your SSL/TLS certificate is valid and not expiring soon. All pages on your site load correctly over HTTPS. All resources (images, scripts, stylesheets, fonts) are served over HTTPS - no mixed content. If using includeSubDomains, every subdomain also works over HTTPS. Your HTTP-to-HTTPS redirect is working correctly (301 permanent redirect).

If any of these are not ready, fix them first. Start with a short max-age (like 300 seconds / 5 minutes) to test safely. Once everything works, increase to the recommended value.

How to add HSTS on Cloudflare

Cloudflare offers two approaches.

Option 1: Cloudflare Dashboard (easiest)

Go to your Cloudflare dashboard, select your site, then navigate to SSL/TLS → Edge Certificates. Scroll down to find the HSTS setting and click “Enable HSTS.” Cloudflare will show you a confirmation dialog explaining the implications. Configure it with max-age of 12 months, includeSubDomains on and preload on.

Option 2: _headers file (for Cloudflare Pages)

If you are using Cloudflare Pages, create or edit a _headers file in your project’s output directory:

/*
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

The /* means this header applies to all pages. Deploy your site and the header will be served on every response.

How to add HSTS on Nginx

Add one line inside your server block for the HTTPS configuration:

server {
    listen 443 ssl http2;
    server_name example.com;

    # Add HSTS header
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    # ... rest of your config
}

The always keyword is important - it ensures the header is sent even on error responses (4xx, 5xx). Without it, Nginx only adds the header on successful responses.

Make sure you only add HSTS to your HTTPS server block, not the HTTP one. The HTTP block should only contain the redirect to HTTPS:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

After editing, test and reload:

nginx -t
systemctl reload nginx

How to add HSTS on Apache

Enable the headers module if it is not already, then add the header in your VirtualHost configuration or .htaccess file:

# Enable module (if not already)
# a2enmod headers

<VirtualHost *:443>
    ServerName example.com

    # Add HSTS header
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

    # ... rest of your config
</VirtualHost>

Or in .htaccess (if your host supports it):

<IfModule mod_headers.c>
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</IfModule>

Restart Apache after changes:

systemctl restart apache2

How to add HSTS on other platforms

Vercel - add to vercel.json:

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [
        {
          "key": "Strict-Transport-Security",
          "value": "max-age=31536000; includeSubDomains; preload"
        }
      ]
    }
  ]
}

Netlify - add to _headers file or netlify.toml:

[[headers]]
  for = "/*"
  [headers.values]
    Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"

AWS CloudFront - use a Response Headers Policy in the CloudFront distribution settings. Navigate to Policies → Response headers → Create policy → Security headers → Enable Strict-Transport-Security.

WordPress - if you cannot edit the server config, a headers plugin such as “HTTP Headers” or “Really Simple SSL” can add HSTS through PHP. Server-level configuration is still preferred.

Understanding the HSTS directives

The full recommended HSTS header looks like this:

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

Each part serves a specific purpose:

max-age=31536000 tells the browser how long to remember the HSTS policy, in seconds. 31536000 seconds equals one year. During this time, the browser will never attempt an HTTP connection. The timer resets on every visit, so regular visitors stay on HTTPS indefinitely. For initial testing, use max-age=300 (5 minutes). Once confirmed working, increase to a full year.

includeSubDomains extends the policy to every subdomain under your domain. If you set HSTS on example.com with includeSubDomains, then api.example.com, blog.example.com and staging.example.com are all forced to HTTPS as well. Only add this if every subdomain supports HTTPS. One subdomain without a valid certificate will become completely inaccessible.

preload signals that you want your domain added to browser HSTS preload lists. These are lists compiled into Chrome, Firefox, Safari and Edge that force HTTPS from the very first visit - not just after the browser first sees your header. To actually get preloaded, you need to submit your domain at hstspreload.org after adding the header.

Fixed it on one site? Run the free check on the other sites you manage - it takes a few seconds and needs no account.

Common mistakes when enabling HSTS

Setting max-age too high on the first try. If you discover a problem after setting a one-year max-age, visitors’ browsers will refuse HTTP connections for up to a year. Start with 5 minutes, then 1 day, then 1 week, then 1 year.

Adding includeSubDomains when subdomains are not ready. If staging.example.com runs on HTTP or has an expired certificate, it becomes unreachable. Audit all subdomains first.

Only adding HSTS to some pages. HSTS should be on every response from your domain. Use the wildcard path (/* in Cloudflare/Netlify) or add it at the server level in Nginx/Apache.

Adding HSTS to HTTP responses. The header should only be served over HTTPS. Browsers ignore HSTS headers received over HTTP (for good reason - anyone on the network could inject a fake one). Your HTTP server block should only redirect to HTTPS.

Forgetting about cookies. HSTS covers the transport layer. You should also set the HTTPS-only cookie flag (Secure) on every cookie so that cookies are never sent over plain HTTP. The two work together.

HSTS preloading explained

The HSTS preload list is a registry of domains that browsers treat as HTTPS-only from the very first connection. Without preloading, a user’s first visit still starts over HTTP because the browser has not seen your HSTS header yet. Preloading closes that gap.

To qualify for preloading, your site must serve a valid HSTS header with max-age of at least one year (31536000), include the includeSubDomains directive, include the preload directive and redirect all HTTP traffic to HTTPS.

Submit your domain at hstspreload.org after meeting these requirements. The submission is reviewed and, once accepted, your domain is hardcoded into browser source code. This process takes weeks to months - browsers need to ship updates that include the new list.

A warning: removing your domain from the preload list is difficult and slow. Only preload domains you are certain will stay on HTTPS permanently.

How to verify HSTS is working

After deploying, verify with any of these methods:

Using curl:

curl -I https://yoursite.com

Look for Strict-Transport-Security in the response headers. You should see your full policy with max-age, includeSubDomains and preload.

Using browser DevTools:

Open your site in Chrome or Firefox, press F12, go to the Network tab, click the first request (your domain) and check the Response Headers section.

Using Guardr:

Guardr reports a missing header, a short max-age and a missing includeSubDomains as separate findings, each with the fix for your platform. Guardr also keeps watch after the fix ships, so a max-age that gets lowered in a later deploy comes back as a new finding. If you have used a one-off checker before, here is how Guardr compares with SecurityHeaders.com.


HSTS is one of the highest-impact header fixes you can make. It takes one line of configuration, costs nothing and works for every visitor on every visit. If your site is served over HTTPS (and it should be), there is no reason not to enable it.

A missing HSTS header rarely travels alone. If your report flagged the other two common header findings as well, the fixes are here: how to fix a missing Content-Security-Policy header and how to fix a missing X-Frame-Options header.

Start with a short max-age, verify everything works, then commit to a full year. Your visitors get a clean first connection. Your grade goes up too.

Check your site health grade

Free check - no sign-up required.

Scan your site →