# HSTS in WordPress: five minutes first, then six months

HSTS in WordPress, explained: what the header does, why to prove it at five minutes, what undo cannot reach, and how it works behind a proxy.

By BetterShield Team, Oct 11, 2026. Updated Nov 2, 2026 by Nazmul Hasan Rupok.

HSTS in WordPress comes down to one response header, Strict-Transport-Security, that tells browsers to use only HTTPS for your site for a set time. Once a browser has seen it, it stops trying plain HTTP for your site at all. Unlike most security headers, its effect stays in visitors' browsers after you take it away.

That last part is why the order you do things in matters. This guide covers what the header does, what max-age, includeSubDomains and preload mean, why a five-minute trial comes first, what HTTPS behind a proxy changes, and the exact steps in BetterShield. For the other headers a site can send, see [WordPress security headers, explained](/blog/wordpress-security-headers/).

## Quick summary

- HSTS tells a browser to use only HTTPS for your site until a set time runs out, and to refuse plain HTTP even when the certificate has a problem.
- Turning it off stops the header, but it cannot reach browsers that already saw it. A five-minute trial limits the new policy duration; existing policies or cached headers can still apply.
- `includeSubDomains` covers every host name under the one that sent it. Choose it only when every one of them answers on HTTPS.
- Preload asks for your domain to be built into browsers. BetterShield never adds it.
- Behind a proxy that handles the encryption, WordPress has to be told the request was secure, or the header is never sent.

## What HSTS does

Without HSTS, a browser can still ask for your site over plain HTTP: an older browser following an http:// link or a typed address, for example, or a newer one that falls back to HTTP when its HTTPS attempt fails. Your server answers with a redirect to HTTPS, and the visit carries on securely. But that plain request travels unencrypted, and anything on the network between the visitor and your server can answer it in your server's place.

HSTS removes that step for browsers that know your site. Once a browser has received the header over HTTPS:

- It rewrites http:// to https:// for your host name before anything leaves the browser: typed addresses, links, and files the page loads.
- It refuses plain HTTP for your site until the time in the header runs out.
- It treats a certificate error as final. The visitor gets an error page with no way past it.

Two limits are worth knowing. A browser has to visit once over HTTPS to learn the rule, so a first visit is not covered. And the rule belongs to the exact host name that sent it: a header from `www.example.com` says nothing about `example.com`.

## Why start with five minutes

Most security headers work one response at a time: remove one and the next page is back to how it was. HSTS is remembered. A browser that saw a six-month setting insists on HTTPS for six months after its last response carrying that header, and only a newer header it receives on a later visit can change that.

That is harmless while HTTPS works everywhere it should. It hurts when something you forgot still needs plain HTTP, or HTTPS stops working: a page or file that only loads over plain HTTP, a subdomain nobody moved (if you cover subdomains), a certificate that fails to renew. With a long setting, that breaks for every visitor who has seen the header, for months, and switching the header off does not help them.

A five-minute setting behaves the same in every way but one: if something breaks, browsers forget the rule five minutes after their last response carrying that header. So send five minutes, use the site the way visitors do, and only then decide on six months. During the trial:

- Type your domain without https:// and check you land on the HTTPS home page.
- Sign in, use the dashboard, send a form, and place a test order if you run a store.
- If you plan to cover subdomains, open each one over HTTPS.

## max-age, includeSubDomains and preload in plain terms

| Part | What it means | What BetterShield sends |
|---|---|---|
| `max-age` | How many seconds the browser should insist on HTTPS. Each response carrying HSTS refreshes the expiry. | `300` (five minutes) or `15768000` (six months) |
| `includeSubDomains` | The rule also covers every host name under the one that sent it. | Only when you choose **Every subdomain as well** |
| `preload` | A request to be written into a list browsers ship with, so even a first visit uses HTTPS. | Never |

### includeSubDomains

It covers names under the host that sent the header. For a site at `example.com`, that means `www`, `shop`, `staging`, `mail` and anything else under it, including names set up years ago. For a site at `www.example.com`, it covers only names under `www`.

Before choosing it, list every subdomain in your DNS and open each one over HTTPS. One that answers only on plain HTTP stops opening in every browser that saw the header. BetterShield's own field says it plainly: "Only if every subdomain you have is on HTTPS too."

### preload

Preload goes further: your domain is added to a list compiled into browsers, so even a first visit uses HTTPS. Getting on the list is a submission you make, and getting off it is slow. In the fix's words: "Never adds preload: that is a submission to a list built into browsers, and removal from it takes months." If you ever want it, treat it as a separate decision to make with your host after living with the long setting.

## Before you turn it on

- **The site address is HTTPS.** Check **Settings › General** in WordPress. BetterShield does not offer the fix until it is: the row reads **Not available here**.
- **The certificate renews on its own.** BetterShield's audit reports **The site's certificate is close to expiring** from 21 days before expiry, and the fix asks you to renew first when expiry is that close.
- **WordPress knows when a request is encrypted.** On many hosts it does without help. Behind a proxy, read the next section.

## HTTPS behind a proxy, load balancer or CDN

Many hosts put something in front of WordPress that handles the encryption, then pass the request on over plain HTTP. WordPress does not read X-Forwarded-Proto, so unless the server or wp-config.php sets the HTTPS variable, it believes every request arrived over plain HTTP. Browsers ignore HSTS on a plain-HTTP response, so BetterShield sends the header only on requests WordPress sees as encrypted, and in this setup it never goes out.

BetterShield spots the mismatch. When the site address is HTTPS but the request reached WordPress as plain HTTP, the row explains that "applying this changes nothing" until wp-config.php sets the HTTPS server variable for those requests.

When your front end sends an `X-Forwarded-Proto` header, the usual fix is a few lines in wp-config.php, above the line that says to stop editing:

```php
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
	$_SERVER['HTTPS'] = 'on';
}
```

Use it only when your server can be reached through that front end alone, because otherwise anyone can send the header. Ask your host which header theirs sends; some already set the variable for you. If your CDN or host can add HSTS itself, choose one place to send it from. The [Server compatibility](/docs/server-compatibility/) guide covers the rest of this setup.

## How to turn on HSTS in WordPress with BetterShield

The fix is **Strict HTTPS (HSTS)**, under **What browsers are told** on **BetterShield › Protection › Hardening**.

1. Leave **How long browsers should insist** at **Five minutes, to prove it works** and **Include subdomains** at **This domain only**. Both are preselected.
2. Press **Preview the change**. Its first line ends with the exact header, `max-age=300`, and nothing is changed.
3. Turn the switch on. The row shows the value with a **Copy** button, and "Confirmed on a page a visitor received" once a visitor has loaded a page over HTTPS.
4. Work through the trial list above.
5. To move to six months, turn the switch off, choose **Six months, once you have lived with the short one**, and turn it on again. The header becomes `max-age=15768000`.

It goes out on front-end pages, the sign-in page, the dashboard and the REST API, on HTTPS requests only. If WordPress or another plugin already sets the header, BetterShield leaves it exactly as it is.

From the terminal, `wp bettershield harden hsts --user=<login>` applies five minutes on this domain only, and `--mode=long` asks for six months. A connected AI assistant may apply only the five-minute, this-domain-only form on its own; six months, or every subdomain, needs your agreement first. Every option is in the [HSTS](/docs/hsts/) guide.

## What undo can and cannot reach

Turning the switch off stops the header at once. Browsers that already saw it keep their rule until their own period ends, counted from their last HSTS response.

| What a browser saw | After you turn the fix off |
|---|---|
| `max-age=300` | That policy expires five minutes after the last HSTS response, unless another applicable policy remains. |
| `max-age=15768000` | It keeps insisting on HTTPS for up to six months. |
| Either, with `includeSubDomains` | The same, for every host name under yours. |

One thing does reach them: a newer header. A browser replaces its stored rule each time it receives the header over HTTPS. So to step down from six months, do not leave the fix off. Turn the switch off, choose **Five minutes, to prove it works** (and **This domain only**, if you had chosen every subdomain), and turn it straight back on. Browsers that come back pick up the short rule; browsers that never come back keep the old one until it runs out.

Testing with a clean browser profile can help separate stored policy from the current server response. Clearing local browser state never repairs visitors' policies or a broken certificate.

What nothing can reach is a visitor whose browser holds a six-month rule while your HTTPS is broken. Preventing that is what the trial and a self-renewing certificate are for.

## Confirm the host and the header you are changing

Test the hostname visitors actually use. The apex domain, `www` and a staging hostname can have different policies, redirects and certificates. HSTS applies to browser connections to those hosts; a `mail` subdomain matters if someone uses its web interface, not because HSTS configures SMTP or IMAP.

A read-only header check for your own site is:

```sh
curl -sS -D - -o /dev/null https://example.com/
```

Replace the example host with yours and inspect the `Strict-Transport-Security` value. Check redirect destinations separately. This reads a response; it does not prove certificate renewal is reliable or reproduce every browser's stored policy.

**Worked example (fictional):** the production host has valid HTTPS but `staging.example.com` serves an old internal page over HTTP. Keep **This domain only** while the host team fixes or retires staging. Do not include every subdomain merely because the production home page loads correctly.

For rollback, distinguish three jobs: stop sending the unwanted policy, make valid HTTPS available, and let browsers receive the revised instruction. A host can send `max-age=0` over valid HTTPS to remove that host's learned policy, but this does not erase a parent's coverage, independently stored subdomain policies or preload. BetterShield's short setting reduces the duration for browsers that receive it. [MDN explains the browser's stored HSTS policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Strict-Transport-Security#expiration).

## Common mistakes

- **Starting at six months.** The five-minute trial costs nothing and shows what still needs plain HTTP.
- **Choosing every subdomain without a list.** A forgotten staging or mail subdomain on plain HTTP stops opening for months.
- **Assuming it works behind a proxy.** Read the row. If WordPress sees plain HTTP, the header never goes out.
- **Leaving it off to shorten it.** That leaves the long rule in place. Turn it off, choose five minutes, turn it straight back on, and keep it on for a while.
- **Sending it from two places.** If your host or CDN adds HSTS too, pick one, so visitors get one header.

## Frequently asked questions

### Does BetterShield add HSTS preload?

No. Preload writes your domain into a list browsers ship with, and removal takes months, so the fix never adds it.

### What happens if my certificate expires while HSTS is on?

Visitors whose browsers saw the header get an error page they cannot click past until the certificate is renewed. BetterShield's audit reports **The site's certificate is close to expiring** from 21 days before: Medium at first, High within 7 days and Critical within 2. Renewing is done with your host or certificate provider.

### My site already redirects to HTTPS. Do I still need HSTS?

The redirect and HSTS work together. The redirect answers a plain-HTTP request once it arrives; HSTS stops a browser that knows your site from sending one. Keep the redirect either way, because first visits still rely on it.

### Does HSTS work on a multisite network?

Each site turns the fix on for itself. On the main site of a subdomain network, **Every subdomain as well** covers every site, and the field says all of them must already be on HTTPS. On a subdirectory network all sites share one host name, so a header from any of them covers the rest.

## Conclusion

HSTS in WordPress is a small header with a long memory. Make sure the site address is HTTPS and WordPress can tell when a request is encrypted, prove it at five minutes, then move to six months. Leave subdomains out unless you know each one, and leave preload alone.

BetterShield is free on [WordPress.org](https://wordpress.org/plugins/bettershield/). The [HSTS](/docs/hsts/) guide covers every option, the WP-CLI flags and each line the row can show.
