In short
HSTS (HTTP Strict Transport Security) is an HTTP response header with which a server instructs the browser to address a domain exclusively via HTTPS for a specified period. After the first visit over an encrypted connection, the browser remembers the rule and locally rewrites every later http:// request to https:// before a single unencrypted byte leaves the device.
The mechanism is defined in RFC 6797 from 2012. HSTS is one of the security headers that can be set with a single line of web server configuration and yet are missing from a large proportion of websites.
How HSTS works
Without HSTS, a typical first visit goes like this: the user types yourshop.com into the address bar, the browser adds http://, sends an unencrypted request, and the server responds with a 301 redirect to the HTTPS address. Only the second request is encrypted. This one open request is the problem. On a public Wi-Fi network, anyone reading the traffic can forge the response, steal session cookies or direct the user to a copy of the page. The class of attack is called SSL stripping; the tool for it, sslstrip, was presented publicly back in 2009.
HSTS closes this window. On the encrypted connection, the server sends the header:
Strict-Transport-Security: max-age=31536000; includeSubDomains
The browser then stores an entry for the domain. As long as the time specified in seconds in max-age has not expired, every request to this domain is rewritten to HTTPS internally. This applies to typed addresses, to links from newsletters and price comparison sites, to bookmarks and to resources embedded by another page. In addition, the browser behaves differently with certificate errors: normally, a user may click away a warning about an expired or incorrect certificate. With an HSTS domain, this is no longer possible. The browser aborts without offering an exception.
Three properties of the mechanism determine how it is handled in practice. First, the browser only evaluates the header if it arrives via HTTPS with a valid certificate; an HSTS header on an HTTP response is ignored, so the redirect to HTTPS must be in place first. Second, the duration is reset with every further response carrying the header, so a regular visitor never loses the entry. Third, according to RFC 6797, the browser only processes the first HSTS header in a response. If the application sets one value and the web server a second, the one that comes first in the response counts.
The three directives
| Directive | Effect | Recommendation |
|---|---|---|
max-age | Validity period of the rule in seconds, counted from the last response with the header | OWASP: 63072000 (two years); preload list: at least 31536000 (one year) |
includeSubDomains | Extends the rule to all subdomains, including those the browser has never visited | Set it as soon as every subdomain really speaks HTTPS |
preload | Signals consent to inclusion in the list built into browsers | Only after several weeks of a stable two-year value |
max-age is the only mandatory value. A value of 0 is also valid and deletes the stored entry in the browser; that is the only way back if an overly broad rule locks systems out. includeSubDomains is the directive with the greatest potential for error, because it affects systems that nobody has in view: the intranet under a subdomain, a staging system with a self-signed certificate, an old statistics server. After the first visit to the main domain, all of these are unreachable for the browser concerned, and for the full duration.
Preload: HSTS before the first visit
HSTS works on the principle of "trust on first use". The very first request to a domain remains unprotected because the browser does not yet know the rule. The preload list closes this gap. It is a list of domains embedded in the Chromium source code for which the browser applies HSTS even without a previous visit; Firefox, Safari and Edge adopt this list. It is operated via hstspreload.org.
The admission requirements are the best real-world example of what a complete HSTS configuration looks like. hstspreload.org requires:
- a valid certificate for the domain,
- a redirect from HTTP to HTTPS on the same host, if the server responds on port 80 at all,
- HTTPS on all subdomains, expressly including the
wwwsubdomain if a DNS record exists for it, - an HSTS header on the base domain with a
max-ageof at least 31536000 seconds, withincludeSubDomainsand withpreload.
The operators of the list give a clear warning on the same page: inclusion is permanent for practical purposes. A domain can be removed again, but the removal only reaches users with the next browser release and takes months to arrive across the board. Anyone with a subdomain that has to speak HTTP for technical reasons does not submit the domain. Full stop.
For an application that does not yet send an HSTS header, hstspreload.org therefore recommends a staged plan: first bring all subdomains onto HTTPS and check the redirect. Then set max-age=300, i.e. five minutes, and observe. Then one week (604800), then one month (2592000), then two years (63072000). Wait for the full duration between the stages so that an error does not get stuck in browsers for a year. Only when the two-year value with includeSubDomains has run for several weeks without incident is preload added and the domain submitted.
Why HSTS matters for online shops
A shop has two things that an attacker on open Wi-Fi wants: the session of the logged-in customer and the payment data in the checkout. Both travel via cookies and forms. Without HSTS, a single http:// link, for example from an old newsletter or a price comparison portal, is enough for the first request to go out unencrypted. With HSTS, this is ruled out after the first HTTPS visit. No other security header has such a good ratio of effort to effect. Even so, according to the HTTP Archive Web Almanac 2025, only 36 per cent of pages crawled on mobile carry an HSTS header.
When changing platforms, HSTS belongs on the checklist next to the 301 redirects. The old http:// URLs from search engines, links and campaigns remain in circulation for years; HSTS ensures that they are not requested unencrypted at all by returning visitors. For payment processing there is a second aspect: a payment service provider embeds its scripts and frames exclusively via HTTPS, and PCI DSS audits ask about the transport encryption of all pages on which payment data is entered. HSTS is no substitute for this, but it is proof that a downgrade is ruled out.
HSTS in Shopware 6
Shopware 6 sets the header itself in the core. The CoreSubscriber appends the value max-age=31536000; includeSubDomains to every successful response that Shopware recognises as an HTTPS request, together with X-Frame-Options: deny, X-Content-Type-Options: nosniff and Referrer-Policy: strict-origin-when-cross-origin. Two consequences follow from this.
First, the staged plan is obsolete for the HTML pages of a Shopware shop: one year with includeSubDomains is already live there, and because the browser only evaluates the first HSTS header, a shorter value on the web server changes nothing. With Shopware shops, subdomains without HTTPS already lock out every visitor who has called up the main domain once. Rolling back via max-age=0 on the web server does not work either, because the core header comes first in the response. The only lasting fix is to bring the affected subdomain onto HTTPS.
Second, the core header only applies to responses that pass through PHP. Theme files, media and thumbnails are delivered directly by the web server, and these responses lack everything that the core sets. The web server should therefore carry the same or a longer value. Then there is HTTPS detection: behind a load balancer or Cloudflare that terminates TLS and speaks HTTP internally, Shopware only recognises the request as secure if TRUSTED_PROXIES is set correctly. If it is not, the core omits HSTS without it being noticeable in the browser. A scan with MDN's HTTP Observatory or a curl -I on the home page, a CSS file and a 404 page shows where the header is missing.
Typical mistakes and distinction
The most common mistake is confusing HSTS with the HTTP-to-HTTPS redirect. The redirect happens after the browser has sent the unencrypted request. HSTS prevents this request from being sent at all. Both are needed together: the redirect for the first visit, HSTS for all visits afterwards.
The second mistake is setting includeSubDomains too early. The directive affects subdomains that the browser has never seen, and it applies for the full duration. Anyone who sets it first needs a complete list of all DNS records under the domain and the certainty that every one of them speaks HTTPS with a valid certificate. The third mistake is submitting a preload request too early, because it cannot be withdrawn at short notice.
HSTS needs to be distinguished from two related concepts. HTTP Public Key Pinning (HPKP) additionally bound a domain to specific certificate keys; Chrome removed the header in 2019 and Firefox in 2020 because of the risk of lock-out, and Safari never supported it. And the Content Security Policy has a directive, upgrade-insecure-requests, that upgrades embedded HTTP resources to HTTPS; it complements HSTS in the case of mixed content but does not replace it, because it only works within a page that has already been loaded. HSTS does not help at all against injected scripts, i.e. cross-site scripting; that is the job of the CSP.
Frequently asked questions
Isn't a 301 redirect to HTTPS enough?
No. The redirect only takes effect after the unencrypted request has left the browser. HSTS prevents precisely this request on every visit after the first.
How do I remove HSTS again if I have locked out a system?
Set the header to max-age=0 and call up the main domain once in the affected browser; the browser then deletes the entry. This only works per browser, only if the domain is not on the preload list and, for Shopware shops, not via the web server, because the core header comes first.
Do I also have to set HSTS if Shopware already sends it?
Yes, on the web server with the same or a longer value. The core header only appears on responses that pass through PHP; static files and error pages do not get it. Behind a TLS-terminating proxy, it is missing entirely if TRUSTED_PROXIES is not correct.
Which max-age value should I use?
For an application that does not yet send a header: start with 300 seconds and work up via one week and one month to two years. For Shopware: at least 31536000, because the core already delivers this value.