/ โ† Security Audit Fixes / HSTS Missing From HTTPS Server
In this guide
  1. What the finding means
  2. Why a redirect alone is not enough
  3. How to check your current headers
  4. Adding the header on each server
  5. Rolling out max-age in stages
  6. A worked example
  7. The fix, step by step
  8. Edge cases worth knowing
  9. Keeping it fixed
  10. Questions and answers

What the finding means

HTTP Strict Transport Security, defined in RFC 6797, is a response header that tells a browser to use only https for your host for a set number of seconds. Once a browser has seen it, it rewrites any http request for your site to https internally, before anything leaves the device, and it refuses to let the visitor click through a certificate warning.

Scanners name the finding in slightly different ways. Nessus reports HSTS Missing From HTTPS Server, OWASP ZAP reports Strict-Transport-Security Header Not Set, and other audits simply list the header as missing. They all mean the same thing: an https response from your server had no Strict-Transport-Security header.

A typical header looks like this:

Strict-Transport-Security: max-age=31536000; includeSubDomains
DirectiveWhat it doesWhen to use it
max-ageHow long, in seconds, the browser remembers the rule. 31536000 is one yearAlways; start low, then raise it
includeSubDomainsApplies the rule to every subdomain of the host that sent itOnly once every subdomain works over https
preloadSignals consent to be added to the browser preload listOnly if you intend to submit the domain and can commit to https permanently

Browsers ignore the header when it arrives over plain http, because an attacker could inject or strip it there. It only takes effect when received over a valid https connection. That is why the finding is about your https server, and why your http to https redirect is still needed.

Why a redirect alone is not enough

Most sites already redirect http:// to https://. The weakness is the request that comes before the redirect. When someone types your domain without a scheme, or follows an old http link, the browser sends that first request in clear text. On public Wi-Fi or any network someone else controls, an attacker can answer it instead of your server, keep the visitor on an http copy of the site and read or change what they send. This is known as SSL stripping.

With HSTS in place, a returning visitor's browser never makes that clear-text request. It upgrades the address itself, which also saves a redirect round trip on every visit. The remaining gap is the very first visit, before the browser has seen the header, which is the gap the preload list closes.

SituationWithout HSTSWith HSTS
Visitor types example.comhttp request sent, then redirectedBrowser goes straight to https
Old http link clickedClear-text request can be interceptedUpgraded before leaving the device
Certificate errorVisitor can click through the warningWarning cannot be bypassed
First visit everExposedStill exposed unless the domain is preloaded

If your redirects themselves are messy, with several hops or a hop back to http, fix those first. The https redirects guide and the guide to a redirect chain that contains http cover them.

How to check your current headers

Ask the server directly with curl, so you see exactly what it sends rather than what a plugin claims to send:

curl -sI https://example.com/ | grep -i strict-transport
curl -sI https://www.example.com/ | grep -i strict-transport
curl -sI https://example.com/no-such-page-404 | grep -i strict-transport
curl -sI http://example.com/

The first three commands should each print one strict-transport-security line once the fix is in. Testing a missing page matters, because many configurations only add headers to successful responses. The last command checks the plain http response: you want a 301 or 308 redirect to the https address of the same host, and it does not matter whether the header appears there.

Before adding includeSubDomains, list every subdomain in your DNS zone, including old ones such as mail, webmail, staging, shop or intranet, and test each over https:

for h in www mail webmail staging shop; do
  printf '%s: ' "$h"
  curl -s -o /dev/null -w '%{http_code}\n' "https://$h.example.com/" || echo failed
done

Any subdomain that fails, shows a certificate error or only serves http will become unreachable for visitors who have seen an includeSubDomains header from the main domain. Fix its certificate first, using the SSL errors guide if needed, or leave includeSubDomains out for now.

๐Ÿ’ก In Chrome you can see whether your browser holds an HSTS rule for a host at chrome://net-internals/#hsts. The same page lets you delete the rule from your own browser while testing.

Adding the header on each server

Add the header in one place only, on the https side. The examples start with a five-minute max-age so a mistake costs little; you raise it later.

nginx

server {
    listen 443 ssl;
    server_name example.com www.example.com;
    add_header Strict-Transport-Security "max-age=300" always;
    # ...
}

The always parameter sends the header on error responses as well as successful ones. Test and reload with sudo nginx -t && sudo systemctl reload nginx. More examples are in the nginx security headers guide.

Apache

<VirtualHost *:443>
    ServerName example.com
    Header always set Strict-Transport-Security "max-age=300"
</VirtualHost>

This needs mod_headers. On Debian and Ubuntu enable it with sudo a2enmod headers, then run sudo apachectl configtest and reload Apache. On shared hosting you can put the same Header always set line in .htaccess. See the Apache security headers guide.

Plesk

Either turn on HSTS in the SSL It! extension for the domain, or go to Websites & Domains, then Hosting & DNS, then Apache & nginx Settings, and add the nginx line to the Additional nginx directives box. Use one method, not both. The Plesk security headers guide walks through the screens.

Cloudflare

In the dashboard, open SSL/TLS, then Edge Certificates, and enable HTTP Strict Transport Security. Choose the max-age and whether to apply it to subdomains. Cloudflare then adds the header at the edge for proxied hostnames. See the Cloudflare security headers guide.

IIS

<system.webServer>
  <httpProtocol>
    <customHeaders>
      <add name="Strict-Transport-Security" value="max-age=300" />
    </customHeaders>
  </httpProtocol>
</system.webServer>

Newer IIS versions also have a built-in HSTS setting on the site, which is the cleaner choice where it is available.

Rolling out max-age in stages

A browser that receives a one-year max-age keeps the rule for a year from the last time it saw the header, even if you remove it from the server. If something then breaks on https, those visitors cannot fall back to http. Raising the value in stages limits the damage of a mistake.

StageHeader valueHow long to watch
1max-age=300A day: check pages, forms, logins and payment pages
2max-age=86400About a week
3max-age=2592000 (30 days)A few weeks, then add includeSubDomains if every subdomain passed
4max-age=31536000; includeSubDomainsLeave in place

Many scanners treat a max-age below about six months as weak, so the finding may change from missing to a warning about a short value until you reach stage four. That is expected.

โš ๏ธ To undo HSTS, send max-age=0 over https. Each browser only forgets the rule when it next visits and sees the new value, so visitors who do not return stay on the old rule until it expires.

A worked example

A small agency's site on a Plesk server running nginx is flagged with HSTS missing from HTTPS server. curl shows the header absent on both the home page and a 404 page. The DNS zone has www, mail and a forgotten dev subdomain.

  1. They add add_header Strict-Transport-Security "max-age=300" always; to the Additional nginx directives for the main domain and apply the change.
  2. curl now shows the header on the home page and the 404 page. A day of normal use shows no problems.
  3. Testing subdomains shows dev has an expired certificate. They issue a new certificate for it in Plesk and confirm it loads over https.
  4. They raise the value to max-age=86400, then to 30 days, then to max-age=31536000; includeSubDomains.
  5. A rescan clears the finding. They leave preload out, because they are not yet sure every future subdomain will run on https.

The fix, step by step

Step 1

Confirm the header is missing

Run curl -sI https://example.com/ | grep -i strict-transport against your bare domain, your www address and a page that returns 404. Note which responses lack the header. Also run curl -sI http://example.com/ and confirm it returns a 301 or 308 redirect to https on the same host. If the redirect is missing or goes through several hops, fix that first. You have finished this step when you know exactly which hosts and response types need the header.

Step 2

Check every subdomain works over https

List every hostname in your DNS zone, including old mail, staging and development subdomains, and load each one over https with curl or a browser. Each must return a page with a valid certificate and no warning. Fix or remove any that fail before you plan to use includeSubDomains. If you cannot fix one yet, leave includeSubDomains out of the header for now, because a failing subdomain would become unreachable for visitors holding the rule.

Step 3

Choose one place to set the header

Decide whether the header will come from the web server, the hosting panel or the CDN, and use only that one. On nginx that is the https server block, on Apache the port 443 virtual host or .htaccess, on Plesk either SSL It! or the Additional nginx directives, and on Cloudflare the HSTS setting under Edge Certificates. Check that no plugin or application already sends the header, so responses never carry two different copies.

Step 4

Add the header with a short max-age

Add the header with max-age=300 first. On nginx write add_header Strict-Transport-Security "max-age=300" always; and on Apache write Header always set Strict-Transport-Security "max-age=300" with mod_headers enabled. Test the configuration with nginx -t or apachectl configtest before reloading. Keeping the first value at five minutes means that if a page, login or payment form breaks on https, browsers forget the rule quickly while you fix it.

Step 5

Verify the header on every response type

Repeat the curl commands for the home page, an inner page, a 404 page, an image or stylesheet, and the www address. Each should show exactly one strict-transport-security line with the value you set. If it is missing on error pages, add the always flag. If it is missing on some paths in nginx, look for add_header lines in location blocks that stop inheritance. Open the Network panel in Chrome DevTools to confirm the browser sees it too.

Step 6

Raise max-age in stages

After a day with no problems, raise the value to max-age=86400, then after a week to 30 days, reloading the server each time and checking with curl. Use the time to test logins, checkout pages, embedded content and any subdomains. When everything has worked at 30 days, move to max-age=31536000. A year is the value most scanners expect, and anything much shorter may still be flagged as weak rather than missing.

Step 7

Add includeSubDomains when ready

Once every subdomain has passed your https checks, change the header to max-age=31536000; includeSubDomains and send it from the bare domain, not only from www. Reload and confirm the new value with curl. This protects subdomains that a visitor has never visited directly, and it is required if you later choose to preload. Keep preload out unless you have decided to submit the domain and can keep every subdomain on https permanently.

Step 8

Rescan and record the change

Run the security scan again and confirm the HSTS finding has cleared. If the scanner now reports a short max-age, check that the final value is live and that no cache or CDN is serving an older response. Write down where the header is set, its value and the date, so anyone rebuilding the server or moving the site keeps it. Add a curl check for the header to your monitoring or deployment notes.

Edge cases worth knowing

Keeping it fixed

HSTS findings come back when a server is rebuilt, a site is migrated, or a new proxy or CDN sits in front of the old configuration. A few checks prevent that:

  1. Keep the header line in your server configuration under version control or in your hosting notes, so a rebuild includes it.
  2. Add a curl check for the header to any deployment or monitoring script you run.
  3. Before creating a new subdomain, make sure it will have a valid certificate from the first day, because the browser rule already applies to it.
  4. Rescan after migrations and after changing CDN or proxy settings.

HSTS is one of several headers audits look for. The security audit fixes cover the others, including Content-Security-Policy and X-Content-Type-Options.

Questions and answers

1. What does HSTS missing from HTTPS server mean?

It means a scanner connected to your site over https and the response did not include a Strict-Transport-Security header. The site itself is encrypted, but browsers have not been told to always use https for it, so a visitor who follows an http link makes one unencrypted request before your redirect. That request can be intercepted on an untrusted network. Adding the header to your https responses tells browsers to upgrade those requests themselves and clears the finding.

2. Is a missing HSTS header a serious security risk?

It is usually rated as low or medium severity rather than critical. The risk is real but specific: an attacker on the same network, such as public Wi-Fi, can intercept the first http request and keep the visitor on an unencrypted copy of the site. Sites with logins, forms or payments benefit most from fixing it. Because the fix is a single header line, it is worth doing on any site that already works fully over https.

3. What max-age should I use for HSTS?

Aim for 31536000 seconds, which is one year, once you have tested the site. Start with a short value such as 300 seconds, raise it to a day, then a month, and only then to a year. Many scanners treat values under about six months as weak, and the preload list requires at least a year. The short start matters because browsers remember the rule for the full max-age, even if you later remove the header.

4. Should I add includeSubDomains to my HSTS header?

Add it only once every subdomain of the domain works over https with a valid certificate. With includeSubDomains, browsers apply the rule to all subdomains, including mail, staging and old test hosts you may have forgotten. Any that only work over http, or have certificate problems, become unreachable for those visitors until the rule expires. List your DNS records and test each hostname first. It is worth adding when you can, because it protects subdomains visitors have never visited directly.

5. Does the HSTS header need to be sent over http as well?

No. Browsers ignore the Strict-Transport-Security header when it arrives over plain http, because an attacker could add or remove it on an unencrypted connection. Send it on your https responses, and keep a permanent redirect from every http address to the https address of the same host. Sending it on http responses as well does no harm, so a single .htaccess line that applies to both is acceptable, but it has no effect there.

6. Should I submit my site to the HSTS preload list?

Only if you are certain the domain and every subdomain will stay on https permanently. Preloading builds your domain into browsers, which protects even the very first visit, but removal depends on browser release cycles and can take months. The list requires a max-age of at least one year, includeSubDomains, the preload directive and a correct http to https redirect. Most small business sites are well protected by a one-year header without preloading.

7. How do I remove or undo HSTS if something breaks?

Change the header to max-age=0 and keep serving it over https. Each browser that visits again sees the new value and forgets the rule. Visitors who do not return keep the old rule until it expires, which is why starting with a short max-age is safer. In your own Chrome browser you can delete the rule straight away from the HSTS section of chrome://net-internals. If the domain is preloaded, you also need to request removal from the preload list.

Check your HSTS and other security headers

Our security audit reads the headers your https responses actually send, flags a missing or weak Strict-Transport-Security value, and shows the other headers that need attention.

Open Security Audit Fixes โ†’
๐Ÿ’ฌ Got a problem?
Related Guides: All Security Audit Fixes  ·  Security Headers on nginx  ·  Security Headers on Plesk  ·  HTTPS Redirects  ·  Learning Hub

About aiwebpageseo

aiwebpageseo.com is a data-driven SEO and AEO (Answer Engine Optimisation) platform providing a free suite of technical SEO tools. Rather than relying on AI-theorised assumptions, the platform analyses live URL performance, delivering objective diagnostics, page speed metrics, CLS debugging, and site crawl data alongside actionable technical tutorials.