HSTS -- HTTP Strict Transport Security
In this tutorial, you'll learn about HSTS. We cover key concepts, practical examples, and best practices to help you understand and apply this topic effectively.
HTTP Strict Transport Security (HSTS) is a security policy mechanism that tells web browsers to always connect to your site over HTTPS, eliminating the possibility of protocol downgrade attacks and cookie hijacking. When a browser receives an HSTS header, it remembers to use HTTPS for your domain for a specified period, refusing to make unencrypted HTTP connections even if the user types http:// manually.
Why HSTS Matters
Without HSTS, a determined attacker can intercept the initial HTTP request and redirect it to a malicious site or downgrade the connection to plain HTTP. This is known as an SSL stripping attack. Even if your site supports HTTPS, the first request can be compromised. HSTS closes this window by instructing the browser to use HTTPS from the very first request. For sites handling login credentials, payment data, or personal information, HSTS is not optional -- it is a fundamental security requirement.
Real-World Use Case
A financial dashboard application experienced a Session Hijacking incident where an attacker exploited an SSL stripping attack on a public Wi-Fi network. The user's initial HTTP request was intercepted and redirected to a lookalike page that captured credentials. After implementing HSTS with a six-month max-age and submitting to the HSTS preload list, the application forced HTTPS at the browser level, making SSL stripping attacks impossible even on untrusted networks.
How HSTS Works
flowchart TD
U[User Types example.com] --> B[Browser Checks for HSTS Policy]
B --> C{Is domain in HSTS list?}
C -->|No| D[Make HTTP Request]
D --> E[Server Returns HTTPS Redirect + HSTS Header]
E --> F[Browser Stores HSTS Policy]
F --> G[Future Requests Use HTTPS Directly]
C -->|Yes| G
style F fill:#27ae60,color:#fff
style G fill:#2ecc71,color:#fff
When a browser receives the Strict-Transport-Security header from your server, it records the policy for the domain. On subsequent visits, the browser automatically upgrades all HTTP requests to HTTPS before sending them. If the HTTPS connection fails (for example, due to a certificate error), the browser displays an error page and refuses to fall back to HTTP.
Enabling HSTS in Cloudflare
# Enable HSTS via Cloudflare API
curl -s -X PATCH "https://api.cloudflare.com/client/v4/zones/YOUR_ZONE_ID/settings/security_header" \
-H "Authorization: Bearer YOUR_API_TOKEN" \
-H "Content-Type: application/json" \
--data '{
"value": {
"strict_transport_security": {
"enabled": true,
"max_age": 31536000,
"include_subdomains": true,
"preload": false,
"nosniff": true
}
}
}' | python3 -m json.tool
# Expected output:
# {
# "result": {
# "id": "security_header",
# "value": {
# "strict_transport_security": {
# "enabled": true,
# "max_age": 31536000,
# "include_subdomains": true,
# "preload": false,
# "nosniff": true
# }
# }
# },
# "success": true
# }
This API call enables HSTS with a max-age of 31536000 seconds (one year). The include_subdomains flag applies the policy to all subdomains. nosniff enables MIME-type sniffing protection. Set preload to true only after you are confident your entire site supports HTTPS without exception.
Verifying HSTS Headers
# Check if HSTS header is being served
curl -sI https://example.com | grep -i "strict-transport-security"
# Expected output:
# strict-transport-security: max-age=31536000; includeSubdomains; preload
# Full header inspection
curl -sI https://example.com | grep -E "HTTP|strict|location"
# Expected output:
# HTTP/2 200
# strict-transport-security: max-age=31536000; includeSubdomains; preload
The HSTS header should appear in every HTTPS response from your site. Use the curl -I command from multiple geographic locations to confirm the header is served globally. If the header is missing, check that Cloudflare's security header setting is enabled and that you are not overriding it with a custom origin header.
HSTS Preload Submission
# Check if your domain is on the HSTS preload list
curl -s "https://hstspreload.org/api/v2/status?domain=example.com" | python3 -m json.tool
# Expected output:
# {
# "status": "preloaded",
# "domain": "example.com",
# "last_updated": "2025-03-15T10:00:00Z"
# }
# If not preloaded, you will see:
# {
# "status": "pending",
# "domain": "example.com"
# }
Submit your domain at hstspreload.org after your HSTS header includes preload and your site serves HTTPS on every subdomain without exception. Chrome, Firefox, Safari, and Edge ship with the preload list built in. Once preloaded, your domain is hardcoded to require HTTPS forever -- removing your domain from the preload list can take months.
Setting HSTS Headers Directly on Origin
# Nginx configuration for HSTS
sudo tee /etc/nginx/conf.d/hsts.conf << 'EOF'
server {
listen 443 ssl;
server_name example.com;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload" always;
# Other SSL settings
ssl_certificate /etc/ssl/certs/origin.pem;
ssl_certificate_key /etc/ssl/private/origin.key;
}
EOF
# Test and reload
sudo nginx -t && sudo systemctl reload nginx
# Expected output:
# nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
# nginx: configuration file /etc/nginx/nginx.conf test is successful
Setting HSTS on the origin server is useful when you need the policy to apply even if Cloudflare's security header is bypassed. The always parameter ensures Nginx sends the header even on error responses. Use a max-age of at least 31536000 (one year) for production domains.
Common Errors and Troubleshooting
HSTS Not Being Served
If the HSTS header is missing from responses, Cloudflare's security header setting may be disabled, or a custom origin header may be overriding it. Solution: verify the setting in Cloudflare dashboard under Security > Edge Certificates, and check that your origin server is not removing the header.
HSTS with Mixed Content
HSTS forces HTTPS, but if your page contains HTTP subresources (images, scripts, fonts), the browser blocks them. Solution: ensure all resources load over HTTPS. Cloudflare's Automatic HTTPS Rewrites can help by rewriting HTTP URLs in the HTML response.
Preload Submission Rejected
HSTS preload requires a max-age of at least 31536000 (one year), the preload directive, includeSubdomains, and valid HTTPS on every subdomain including www. If any subdomain serves HTTP or has an invalid certificate, preload submission is rejected.
HSTS Policy Too Short
A short max-age (minutes or hours) does not provide meaningful protection against SSL stripping. Attackers can wait for the policy to expire. Solution: use at least 31536000 seconds (one year) for production sites.
cannot Remove HSTS During Testing
Once a browser receives an HSTS policy with a long max-age, it remembers it. To clear HSTS in Chrome, go to chrome://net-internals/#hsts and delete the domain. In Firefox, clear site data. This is important during testing when you may need to temporarily disable HTTPS.
Practice Questions
- What is the purpose of the
preloaddirective in an HSTS header? - Why should you avoid enabling HSTS preload on a development or staging domain?
- What is the minimum
max-agerequired for HSTS preload list submission?
FAQ
{{< faq "Can I use HSTS without Cloudflare?">}} Yes. HSTS is a standard HTTP header supported by all modern web servers. You can configure it on Nginx, Apache, Caddy, IIS, and most application frameworks. Cloudflare simplifies HSTS deployment by setting the header at the edge, but the same protection is available by configuring your origin server directly. {{< /faq >}}
Summary
HSTS is a critical security header that forces HTTPS connections and prevents protocol downgrade attacks. Cloudflare provides one-click HSTS configuration through the dashboard and API, supporting configurable max-age, includeSubdomains, and preload directives. HSTS preload offers the strongest protection by baking HTTPS requirements into browsers themselves. Implement HSTS with a one-year max-age and includeSubdomains on all production domains, then submit to the preload list after verifying full HTTPS coverage.
This guide is brought to you by the developers of Cloudflare, Web Security, and Durga Antivirus Pro at DodaTech.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro