# Strict-Transport-Security Header — Syntax, Values & Examples

> Tells browsers to use only HTTPS for the site for a set period, blocking downgrade attacks.

Once a browser has seen HSTS over HTTPS, it rewrites every `http://` request for that domain to `https://` before sending it, for `max-age` seconds. That closes the window in which an attacker on the network could intercept the first plain-HTTP request. Chrome DevTools shows these local rewrites as '307 Internal Redirect'.

- Type: Response header
- Category: Security
- Defined in: [RFC 6797 — HTTP Strict Transport Security](https://www.rfc-editor.org/rfc/rfc6797)

## Syntax

```http
Strict-Transport-Security: max-age=<seconds>[; includeSubDomains][; preload]
```

## Values

| Value | Meaning |
|---|---|
| `max-age=<seconds>` | how long the browser remembers to use HTTPS only |
| `includeSubDomains` | apply to every subdomain too |
| `preload` | consent to inclusion in browsers' built-in HSTS list |

## Examples

```http
Strict-Transport-Security: max-age=31536000; includeSubDomains
```

one year, all subdomains — a common production value

```http
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
```

two years, all subdomains, eligible for preloading

```http
Strict-Transport-Security: max-age=300
```

five minutes — start small while testing

## Good to know

- Browsers ignore HSTS received over plain HTTP; it must arrive over HTTPS.
- `includeSubDomains` covers every subdomain, including internal ones that may not have certificates yet.
- Preloading (hstspreload.org) is slow to undo: removal takes months to reach users.
- Ramp `max-age` up gradually — minutes, then a week, then a year or two.

## How to set Strict-Transport-Security

### Express

```
res.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
```

### Next.js (next.config.js)

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

### nginx

```
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
```

### Apache (.htaccess)

```
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
```

## Check it

Fetch only the response headers and filter for Strict-Transport-Security. In the browser, it's under Response Headers in DevTools → Network.

```sh
curl -sI https://example.com/ | grep -i '^strict-transport-security:'
```

## FAQ

### What does '307 Internal Redirect' mean in Chrome?

HSTS at work: Chrome rewrote http:// to https:// locally because the site sent Strict-Transport-Security earlier or is preloaded. No request reached the server.

### How do I turn HSTS off?

Send Strict-Transport-Security: max-age=0 over HTTPS; browsers that see it forget the policy. Preloaded domains must also be removed from the preload list.

## Status codes it appears with

- [307 Temporary Redirect](https://www.devkult.com/http/307.md)
- [301 Moved Permanently](https://www.devkult.com/http/301.md)

## Related headers

- [Content-Security-Policy](https://www.devkult.com/headers/content-security-policy.md): Restricts where a page may load scripts, styles, images and frames from — the main defence against cross-site scripting.
- [Location](https://www.devkult.com/headers/location.md): The URL to go to next — the target of a redirect, or the address of a newly created resource.
- [X-Content-Type-Options](https://www.devkult.com/headers/x-content-type-options.md): With nosniff, stops browsers guessing a response's type, so a text file can't be run as a script.

## Further reading

- [Security headers checklist](https://www.devkult.com/blog/security-headers-checklist.md)
- [Security headers generator](https://www.devkult.com/tools/security/security-headers-generator.md)

Open the interactive page: https://www.devkult.com/headers/strict-transport-security