Skip to main content
Beta: Front-End Checklist is currently in beta. Some issues are still being fixed. Thanks for your patience.

Set an HSTS header

The Strict-Transport-Security response header tells browsers to always use HTTPS for your domain, preventing protocol downgrade attacks and cookie hijacking.

Utilities
Quick take
Typical fix time 15 min
  • Set `Strict-Transport-Security: max-age=31536000; includeSubDomains` on all HTTPS responses
  • Use `max-age=31536000` (1 year) minimum; HSTS preloading requires at least 1 year
  • Add `includeSubDomains` to protect all subdomains from downgrade attacks
  • Add `preload` only after testing — it is difficult to reverse and takes weeks to propagate
  • Never send the HSTS header over plain HTTP — only over HTTPS
Why it matters: Without HSTS, an attacker on the same network can intercept the first HTTP request and strip TLS (SSL stripping), silently downgrading the connection before the browser ever sees a redirect.

Rule Details

HTTP Strict Transport Security (opens in new tab) (HSTS) instructs browsers to always use HTTPS for your domain, even if the user types an http:// URL or clicks an unencrypted link. Defined in RFC 6797 (opens in new tab), it eliminates the window of vulnerability present in HTTP-to-HTTPS redirects.

Code Example

Strict-Transport-Security: max-age=31536000; includeSubDomains
DirectiveRequiredDescription
max-age=<seconds>YesHow long (in seconds) the browser caches the HSTS policy. Use 31536000 (1 year) at minimum.
includeSubDomainsRecommendedApplies HSTS to all subdomains. Required for HSTS preload submission.
preloadOptionalSignals intent to be in the HSTS preload list. See warnings below.

Why It Matters

Without HSTS, an attacker on the same network can intercept the first HTTP request and strip TLS (SSL stripping), silently downgrading the connection before the browser ever sees a redirect.

How HSTS Works

Without HSTS, the first visit to the HTTP version of your site triggers a redirect to HTTPS. An attacker performing a man-in-the-middle (MITM) attack can intercept that first HTTP request and strip TLS, which is the classic SSL-stripping problem the OWASP HSTS guidance (opens in new tab) is designed to prevent.

With HSTS:

  1. The browser receives Strict-Transport-Security on the first HTTPS response.
  2. It caches the HSTS policy for max-age seconds.
  3. All subsequent navigations to the HTTP version are internally upgraded to HTTPS before any network request is made — the attacker never sees the plain HTTP traffic.

Server Configuration

Nginx

server {
    listen 443 ssl http2;
    server_name example.com;
 
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

Apache

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

Next.js (next.config.js)

/** @type {import('next').NextConfig} */
const nextConfig = {
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          {
            key: 'Strict-Transport-Security',
            value: 'max-age=31536000; includeSubDomains',
          },
        ],
      },
    ]
  },
}
 
module.exports = nextConfig

Express.js (using Helmet)

import helmet from 'helmet'
 
app.use(
  helmet.hsts({
    maxAge: 31536000,
    includeSubDomains: true,
  })
)

HSTS Preloading

The preload directive plus submission to the browser preload list extends the RFC 6797 (opens in new tab) behavior even further by hardcoding your domain into shipping browsers. This means browsers never make a plain HTTP request to your domain, even on first visit.

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Requirements to be accepted into the preload list:

  • Serve a valid TLS certificate
  • Redirect HTTP to HTTPS on the same host
  • Serve HSTS with max-age of at least 31536000 (1 year)
  • Include includeSubDomains
  • Include preload
  • All subdomains must be accessible over HTTPS
Preload Is Difficult to Reverse

Once in the preload list, removal takes weeks to months to propagate. All subdomains must serve valid HTTPS before adding preload. Test thoroughly — a misconfigured subdomain will become inaccessible to browsers in the list.

Common Mistakes

MistakeImpactFix
Sending HSTS over HTTPHeader is ignored; any browser that cached it will be locked outOnly send HSTS over HTTPS
Short max-age (e.g., 300)Provides minimal protectionUse 31536000 (1 year)
Missing includeSubDomainsSubdomains remain vulnerable to downgrade attacksAdd includeSubDomains if all subdomains serve HTTPS
Using preload without testingSubdomains become unreachable if they lack HTTPSOnly add preload after confirming all subdomains work

Exceptions

  • A missing or weak header should be evaluated against the live production response path, not only the framework or server config in isolation.
  • Legacy integrations or embedded third-party content may require narrowly scoped exceptions, but they should be documented explicitly instead of left permissive by default.
  • When multiple security headers are missing, prioritize the header that removes the highest exploitability or browser capability first.

Support Notes

  • The feature is supported across the current project browser matrix.
  • Baseline-compatible minimums: chrome 115, edge 115, firefox 116, safari 16.4, safari_ios 16.4.
  • Add a fallback or a narrower policy note when a required project target falls outside that support range.

Verification

Automated Checks

  • Inspect the effective response headers with curl, a security header scanner, or equivalent tooling against representative live responses.

Manual Checks

  • Verify the browser or user-facing behavior manually in a production-like flow and confirm there is no stronger conflicting security signal.

Use with AI

Copy these prompts to use with your AI assistant, or install the MCP server to use directly from Claude, Cursor, or Windsurf.

Check

Verify implementation

Check whether the server sends a Strict-Transport-Security header on all HTTPS responses, and verify the max-age, includeSubDomains, and preload directives are appropriate.

Fix

Auto-fix issues

Add a Strict-Transport-Security header with max-age=31536000 and includeSubDomains to all HTTPS responses. Configure your web server or CDN to send this header, and validate it with curl or securityheaders.com.

Explain

Learn more

Explain what HTTP Strict Transport Security (HSTS) is, how it prevents SSL stripping attacks, what the max-age and includeSubDomains directives do, and why the preload directive requires caution.

Review

Code review

Review server config, headers, forms, and integration points related to Set an HSTS header. Flag exact responses, cookies, or browser behaviors that violate the rule, and verify them against the effective production-like response.

Sources

References used to support the guidance in this rule.

Further Reading

Tools and supplementary material for exploring the topic in more depth.

Mozilla Observatory
observatory.mozilla.orgTool

Rules that often go hand-in-hand with this one.

Set X-Content-Type-Options: nosniff

The X-Content-Type-Options: nosniff header prevents browsers from MIME-sniffing a response away from the declared Content-Type, blocking a class of drive-by download and XSS attacks.

Security
Implement a content security policy

A Content Security Policy is implemented to prevent XSS attacks and control resource loading.

Security
Set an X-Frame-Options header

The X-Frame-Options header controls whether your page can be embedded in an iframe, frame, or object — preventing clickjacking attacks.

Security
Set a Referrer-Policy header

The Referrer-Policy header controls how much referrer information is sent when navigating from your site to another, protecting user privacy and preventing leaking sensitive URL parameters.

Security

Was this rule helpful?

Your feedback helps improve rule quality. This stays internal for now.

Loading feedback...
0 / 385