Skip to main content
CodeRocket
Securityhighheaders

Set an HSTS header

rule · hsts

HTTP Strict Transport Security (opens in a 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 a new tab), it eliminates the window of vulnerability present in HTTP-to-HTTPS redirects.

Code Example

HTTP
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 a 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

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

Apache

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

Next.js (next.config.js)

JavaScript
/** @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)

JavaScript
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 a 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.

HTTP
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

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.