Type something to search...
What is HSTS and how to enable it?

What is HSTS and how to enable it?

HSTS (HTTP Strict Transport Security) is a security header that tells browsers to only connect to your website over HTTPS, never plain HTTP, for a period of time you choose. Once a browser has seen the header, it automatically upgrades any http:// link to https:// before sending a request, and it refuses to let visitors click past certificate errors. You enable it by sending a Strict-Transport-Security response header from your web server, CDN, or application, starting with a short duration and increasing it once you're confident everything works.

Redirecting HTTP to HTTPS is a good start, but it leaves a small gap on the first request that attackers can exploit. HSTS closes that gap. This article explains what HSTS protects against, what each part of the header means, how to enable it safely on Nginx, Apache, WordPress, and Cloudflare, how HSTS preloading works, and how to avoid locking yourself into a configuration you can't easily undo.

The Problem HSTS Solves

Imagine a visitor types example.com into their browser. Without HSTS, this is what happens:

  1. The browser requests http://example.com over an unencrypted connection.
  2. Your server responds with a 301 redirect to https://example.com.
  3. The browser follows the redirect and loads the secure site.

Step 1 is the weak point. On a hostile network, such as a malicious public Wi-Fi hotspot, an attacker can intercept that first HTTP request and respond themselves, keeping the visitor on an HTTP version of the site that they control. This is known as an SSL stripping or downgrade attack. The visitor may never notice the missing padlock.

With HSTS, once the browser has visited your site over HTTPS and received the header, it remembers the rule. Next time, even if the visitor types example.com or clicks an old http:// link, the browser internally rewrites it to https:// before any network request is sent. The attacker never gets the chance to intercept an insecure request.

HSTS also changes how certificate errors are handled. On an HSTS-protected site, browsers don't show the "Proceed anyway" option for certificate problems, so visitors can't be talked into accepting a fake certificate.

How the HSTS Header Works

HSTS is a single HTTP response header sent over HTTPS:

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

It has three parts:

  • max-age (required): How long, in seconds, the browser should remember to use HTTPS only. 31536000 is one year. Each time the browser sees the header again, the timer resets.
  • includeSubDomains (optional): Applies the rule to every subdomain, such as www.example.com, shop.example.com, and mail.example.com.
  • preload (optional): Signals that you want your domain included in browsers' built-in HSTS preload lists. It does nothing on its own until you submit your domain.

Browsers ignore the header if it's sent over plain HTTP, because an attacker could inject it. It must be delivered over a valid HTTPS connection.

Before You Enable HSTS

HSTS is powerful because browsers enforce it strictly. That also means a mistake can make parts of your site unreachable for the length of your max-age. Check these first:

  1. Your whole site works over HTTPS: Every page, asset, and form should load securely without mixed content warnings.
  2. HTTP redirects to HTTPS: All HTTP requests should redirect to HTTPS with a 301.
  3. Your certificate is valid and auto-renews: If your certificate expires while HSTS is active, visitors will see a hard error they can't bypass. Make sure renewal is automated and monitored.
  4. Subdomains are ready (if you'll use includeSubDomains): Check every subdomain, including ones you rarely think about, like mail., cpanel., staging., dev., internal tools, and old marketing sites. If any of them only work over HTTP, includeSubDomains will break them.

Step 1: Start With a Short max-age

Roll HSTS out gradually. Start with a short duration so a mistake only affects visitors briefly:

Strict-Transport-Security: max-age=300

That's five minutes. Once you've confirmed everything works, increase it in stages, for example:

  1. One week: max-age=604800
  2. One month: max-age=2592000
  3. One year or more: max-age=31536000 (or 63072000 for two years)

Add includeSubDomains only once you've checked every subdomain.

Step 2: Enable HSTS on Your Server

Nginx

Add the header inside your HTTPS server block, not the HTTP one:

server {
    listen 443 ssl;
    listen [::]:443 ssl;
    http2 on;
    server_name example.com www.example.com;

    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

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

    # ... the rest of your configuration
}

The always parameter makes Nginx send the header on error responses too, not just successful ones.

One common gotcha: Nginx's add_header directives are inherited from a parent block only if the child block defines no add_header of its own. If a location block adds any header, such as Cache-Control, the HSTS header from the server block won't be sent for requests matching that location. Either repeat the header in those locations or put shared headers in a snippet file you include wherever needed.

Test and reload:

sudo nginx -t
sudo systemctl reload nginx

Apache

Enable mod_headers if it isn't already:

sudo a2enmod headers

Then add the header to your HTTPS virtual host (the *:443 block):

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com

    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</VirtualHost>

If you only have .htaccess access, you can add it there, but make sure it's only sent on HTTPS responses:

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

Test and reload:

sudo apachectl configtest
sudo systemctl reload apache2

On RHEL-based systems, the service is httpd.

LiteSpeed and Shared Hosting

LiteSpeed servers read Apache-style .htaccess rules, so the .htaccess example above generally works. Some hosting panels also have a toggle for HSTS in their SSL or security settings.

Step 3: Enable HSTS in WordPress

The best place for HSTS is the web server or CDN, because it's faster and applies to every response, including static files. If you can't change server configuration, you can send the header from WordPress using the send_headers action.

Add this to a small custom plugin or your child theme's functions.php:

<?php
/**
 * Send the HSTS header on HTTPS requests.
 */
function sajjad_send_hsts_header() {
    if ( is_ssl() && ! headers_sent() ) {
        header( 'Strict-Transport-Security: max-age=31536000; includeSubDomains' );
    }
}
add_action( 'send_headers', 'sajjad_send_hsts_header' );

This only covers requests handled by WordPress, not images or other static files served directly by the web server, but browsers apply HSTS to the whole host once they've seen it, so it's still effective. Security plugins like Really Simple Security and Solid Security (formerly iThemes Security) also offer HSTS options if you prefer a settings screen.

Step 4: Enable HSTS on Cloudflare

If your site is behind Cloudflare, you can enable HSTS at the edge:

  1. Open your domain in the Cloudflare dashboard.
  2. Go to SSL/TLS > Edge Certificates.
  3. Find HTTP Strict Transport Security (HSTS) and click Enable HSTS.
  4. Read and acknowledge the warning, then choose a Max Age, whether to Apply HSTS policy to subdomains, and whether to include Preload.
  5. Set No-Sniff Header as well if you like; it's offered on the same screen.

Also make sure your SSL/TLS mode is set to Full (strict) and that Always Use HTTPS is on. Avoid sending HSTS from both Cloudflare and your origin with different values, since that can cause confusion when troubleshooting.

Step 5: Verify the Header

Check that the header is being sent, using curl:

curl -sI https://example.com | grep -i strict-transport-security

You should see your header in the output. You can also check in your browser: open developer tools, go to the Network tab, click the main document request, and look under Response Headers.

Online tools like securityheaders.com and Qualys SSL Labs will also report whether HSTS is present and how it's configured.

In Chrome, you can inspect and clear what the browser has stored for a domain at chrome://net-internals/#hsts, which is handy while testing.

HSTS Preloading

HSTS has one remaining gap: the very first visit. If someone has never been to your site, their browser hasn't seen your HSTS header yet, so that first request could still be intercepted.

Preloading solves this. Browsers ship with a built-in list of domains that must always use HTTPS. If your domain is on the list, browsers enforce HTTPS from the very first visit.

To qualify for the Chromium-maintained preload list (used by Chrome, Edge, Firefox, Safari, and others), you must:

  1. Serve a valid certificate.
  2. Redirect HTTP to HTTPS on the same host.
  3. Serve all subdomains over HTTPS, including www if it exists in DNS.
  4. Send the HSTS header on the base domain over HTTPS with:
    • max-age of at least one year (31536000)
    • includeSubDomains
    • preload

The header looks like this:

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

Then submit your domain at hstspreload.org, which checks the requirements and adds eligible domains to the list. It can take weeks or months for the change to reach users through browser updates.

Think Carefully Before Preloading

Preloading is effectively permanent in the short term. Removal is possible, but it can take many months for browsers to ship an update, and users on older browser versions may keep the old list even longer. Before preloading, be sure:

  • Every current and future subdomain will support HTTPS. That includes internal tools and any service you might add later.
  • You won't need HTTP for anything, such as a legacy device, an internal app, or a hosting control panel on a subdomain.
  • You're comfortable with the commitment for your whole organisation, not just the website.

For many small sites, a long max-age without preloading is a perfectly reasonable choice. Preloading makes the most sense for sites handling logins, payments, or sensitive data, where the first-visit gap matters.

How to Undo HSTS

If you need to turn HSTS off, you can't just delete the header, because browsers that already saw it will keep enforcing it until max-age expires. Instead, send a header with a max-age of zero:

Strict-Transport-Security: max-age=0

Browsers that receive this over HTTPS will forget the rule. Keep sending it for a while, because visitors only pick it up on their next HTTPS visit. If your domain is preloaded, you'll also need to request removal at hstspreload.org and remove preload from your header.

This is why starting with a short max-age matters so much. A mistake with a five-minute value fixes itself almost immediately; a mistake with a two-year value does not.

Common HSTS Mistakes

  • Sending the header over HTTP: Browsers ignore it. It must come from an HTTPS response.
  • Adding includeSubDomains too early: A forgotten HTTP-only subdomain becomes unreachable.
  • Letting certificates expire: With HSTS, an expired certificate means a hard error with no bypass.
  • Only setting it on the www host: Preloading requires the header on the base domain, such as example.com.
  • Preloading without a plan: It's slow to undo, so treat it as a long-term commitment.
  • Missing the header in some locations: The Nginx add_header inheritance issue can leave some responses without HSTS.

FAQ: HSTS

HSTS stands for HTTP Strict Transport Security. It's a response header that tells browsers to only connect to your site over HTTPS for a specified period.

No. A redirect happens after the browser has already sent an insecure HTTP request. HSTS makes the browser switch to HTTPS before any request is sent, which closes the gap attackers use for downgrade attacks. You should use both.

Start with a few minutes while testing, then increase to a week, a month, and finally at least one year (31536000 seconds). Preloading requires a minimum of one year.

It can if parts of your site or subdomains don't work over HTTPS, or if your certificate expires. Browsers will refuse to connect, with no bypass. Test thoroughly and roll out with a short max-age first.

Only if you're sure every subdomain will support HTTPS for the long term. Preloading protects first-time visitors, but removal from browser lists takes months, so it's a serious commitment.

Send Strict-Transport-Security: max-age=0 over HTTPS for a while so returning visitors' browsers forget the rule. If you preloaded your domain, also request removal at hstspreload.org.

No. WordPress doesn't send an HSTS header on its own. You can add it at the web server, at your CDN, with a small snippet using the send_headers action, or through a security plugin.


Conclusion

HSTS is a small header with a big effect. It tells browsers to use HTTPS for your site every time, closing the gap left by redirects, preventing downgrade attacks, and stopping visitors from clicking past certificate warnings. Enabling it takes a single line in your Nginx, Apache, Cloudflare, or WordPress configuration.

The key is rolling it out carefully: confirm your whole site and every subdomain work over HTTPS, make sure certificates renew automatically, start with a short max-age, and increase it in stages. Once you're confident, a one-year policy gives you strong protection, and preloading can extend it to first-time visitors if you're ready for the long-term commitment.

Tags :
Share :

Related Posts

What are the best WordPress security plugins?

What are the best WordPress security plugins?

The best WordPress security plugins for most sites are Wordfence, Sucuri Security, Solid Security, MalCare, All-In-One Security (AIOS), Patchstack, a

Dive Deeper
What are the most common website security threats?

What are the most common website security threats?

The most common website security threats are vulnerable or outdated software, weak and stolen passwords, malware infections, injection attacks like S

Dive Deeper
How does GDPR affect website security?

How does GDPR affect website security?

GDPR affects website security by turning it from a good habit into a legal obligation. If your website collects personal data from people in the EU (

Dive Deeper