DEV Community

Cover image for How to Fix Missing HSTS Header (Step-by-Step)
Guardr
Guardr

Posted on • Originally published at guardr.io

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

If you've scanned your website and received a warning about a missing HSTS header, you're not alone. From our internal checks, most of the none technical websites are misconfigured and are missing HSTS header mostly because they are lack of security awareness, this is why we want to raise such awareness. It's one of the most common security findings and one of the most impactful 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's covered

What is HSTS and why does it matter?

HSTS stands for HTTP Strict Transport Security. It's a security header that tells browsers: "Only connect to this site over HTTPS. Never use HTTP."

Without HSTS, here's 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 — is unencrypted and vulnerable. An attacker on the same network (coffee shop Wi-Fi, hotel network, airport) can intercept that first request, steal cookies, inject malicious content, or redirect the user to a fake version of your site. This is called a protocol downgrade attack.

HSTS eliminates this window of vulnerability. Once a browser sees the HSTS header, it remembers: "This site is HTTPS-only." Every future visit goes directly to HTTPS — no HTTP request, no redirect, no attack window.

This isn't a theoretical risk. SSL stripping attacks are well-documented and tools to execute them are freely available. If your site handles any user data — logins, forms, personal information, payments — HSTS should be considered essential.

How HSTS protects your users

HSTS provides three specific protections:

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. This prevents attackers from injecting content through insecure resource loads.

Cookie protection. Without HSTS, cookies set without the Secure flag could leak over an HTTP connection. HSTS ensures the connection is always encrypted, adding a layer of protection even if cookie flags aren't perfect.

Before you enable HSTS

HSTS is powerful, but it's 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 won't see a warning — they'll 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 aren't 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're using Cloudflare Pages, create or edit a _headers file in your project's output directory:

/*
  Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

After editing, test and reload:

nginx -t
systemctl reload nginx
Enter fullscreen mode Exit fullscreen mode

How to add HSTS on Apache

Enable the headers module if it isn't 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>
Enter fullscreen mode Exit fullscreen mode

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

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

Restart Apache after changes:

systemctl restart apache2
Enter fullscreen mode Exit fullscreen mode

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"
        }
      ]
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Netlify — add to _headers file or netlify.toml:

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

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 can't edit server config, use a security plugin like "HTTP Headers" or "Really Simple SSL" which can add HSTS through PHP. However, server-level configuration is always preferred.

Understanding the HSTS directives

The full recommended HSTS header looks like this:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Enter fullscreen mode Exit fullscreen mode

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 protected 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.

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 aren't 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 — an attacker could inject a fake one). Your HTTP server block should only redirect to HTTPS.

Forgetting about cookies. HSTS protects the transport layer, but you should also set the Secure flag on all cookies to ensure they're never sent over HTTP. HSTS and Secure cookies 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 is still vulnerable because the browser hasn't seen your HSTS header yet. Preloading eliminates this 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're 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
Enter fullscreen mode Exit fullscreen mode

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:

Scan your site for free — Guardr checks your HSTS configuration and tells you exactly what's missing or misconfigured. If your HSTS is working correctly, the TLS/SSL score reflects it. If something is off — missing includeSubDomains, short max-age, or HSTS not present at all — you'll see a specific finding with the exact fix.


HSTS is one of the highest-impact security improvements you can make. It takes one line of configuration, costs nothing, and protects every user on every visit. If your site is served over HTTPS (and it should be), there's no reason not to enable it.

Start with a short max-age, verify everything works, then commit to a full year. Your users and your security score will thank you.

Top comments (0)