Featured image of post Enforcing HTTPS Connections Securely using HSTS policiesFeatured image of post Enforcing HTTPS Connections Securely using HSTS policies

Enforcing HTTPS Connections Securely using HSTS policies

Explore strict transport security headers, configuration arguments, subdomains, and preloaded site submissions.

The First-Request Problem

HTTPS encrypts traffic after the 연결 is established, but the initial request to an HTTP URL is still sent in cleartext. An attacker on the same 네트워크 can intercept that first request, perform an SSL stripping attack, and downgrade the user to HTTP for the entire session.

HTTP Strict Transport Security (HSTS) closes this window. Once a browser receives an HSTS header, it automatically upgrades all future HTTP requests to HTTPS and refuses to connect if the certificate is invalid.

How HSTS Works

The 서버 sends the Strict-Transport-Security header in the HTTPS response:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
DirectiveDescription
max-age=<seconds>How long the browser should remember to use HTTPS only
includeSubDomainsApply the policy to all subdomains
preloadSignal inclusion in browser preload lists

Once processed, the browser:

  1. Rewrites all http:// links to https:// for the domain
  2. Refuses to connect if TLS handshake fails or certificate is invalid
  3. Caches the policy for the duration of max-age

Configuring HSTS on Servers

NGINX

server {
    listen 443 ssl;
    ssl_certificate /etc/ssl/certs/example.pem;
    ssl_certificate_key /etc/ssl/private/example.key;

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}

The always parameter ensures the header is sent even on error pages.

Apache

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

Cloudflare

Cloudflare enables HSTS through the dashboard: SSL/TLS > Edge Certificates > HTTP Strict Transport Security. You can toggle preload and includeSubDomains with one click. Cloudflare also offers a “No Sniff” mode that prevents downgrade attacks at the edge.

Express/Node.js

const helmet = require("helmet");
app.use(helmet.hsts({
  maxAge: 31536000,
  includeSubDomains: true,
  preload: true
}));

The HSTS Preload List

The max-age directive only works after the user has visited your site once. The HSTS preload list baked into Chrome, Firefox, Safari, and Edge hard-codes domains that should always use HTTPS, eliminating the first-request vulnerability entirely.

To submit your domain:

  1. Ensure your site returns a valid HSTS header with preload
  2. Go to hstspreload.org
  3. Submit your domain

Requirements for preload inclusion:

RequirementDetail
Valid certificateTLS on all subdomains
max-ageAt least 31536000 (1 year)
includeSubDomainsRequired
preloadPresent in header
RedirectsHTTP → HTTPS for all requests

Warning: Preload inclusion is permanent for the domain. You cannot easily remove your domain from the list once accepted.

Testing HSTS 배포

Several tools verify your HSTS configuration:

# curl with headers
curl -sI https://example.com | grep -i strict-transport

# Security Headers
# https://securityheaders.com

# SSL Labs
# https://www.ssllabs.com/ssltest/analyze.html?d=example.com
# Using the HSTS check script
curl -s https://hstspreload.org/api/v2/check?domain=example.com

Common Pitfalls

IssueConsequenceFix
max-age too shortPolicy expires frequentlySet to at least 1 year
Missing includeSubDomainsSubdomains unprotectedAdd the directive
HTTP-only site serves HSTSHeader ignoredServe only over HTTPS
Mixed contentBrowser blocks insecure resourcesAudit all http:// references
Preload without preparationPermanent HTTPS requirementTest with max-age=0 first

Clearing HSTS During Development

To clear a cached HSTS policy in the browser:

  • Chrome: chrome://net-internals/#hsts → Delete domain
  • Firefox: Clear browsing history for the domain
  • Safari: Clear website data in Preferences

As a developer, use max-age=0 to signal policy revocation:

Strict-Transport-Security: max-age=0

Summary

HSTS is a fundamental security header that ensures HTTPS is always used after the first visit, preventing SSL stripping and downgrade attacks. Configure it with max-age=31536000, includeSubDomains, and optionally preload for full protection. Always test your configuration with security checkers and clear the HSTS cache during development to avoid lockouts.