Website security: HSTS and security headers for your website
With every page your web server sends headers along: short lines with instructions for the browser. They partly determine how safely the browser handles your site. Here you'll read what HSTS is, what other security headers exist, and how you set and test them.
First, your site needs a working certificate and must send visitors to https. You can read about that in Activating, renewing and troubleshooting an SSL certificate and Forcing visitors to https.
What does HSTS do?
Redirecting to https has one weak moment: the first request via http still travels unencrypted. Someone on the same network, for example on public wifi, can intercept that request. HSTS (HTTP Strict Transport Security) solves that. Your site sends this header along via https:
Strict-Transport-Security: max-age=31536000
The browser then remembers that it may only open your site via https. If a visitor later types http://, the browser itself turns it into https, even before anything goes over the network. The browser ignores an HSTS header that arrives via plain http.
| Component | Meaning |
|---|---|
max-age | How long the browser remembers it, in seconds. The internet.nl test counts at least one year (31536000) as sufficient. |
includeSubDomains | Also applies to all subdomains, such as shop.yourdomain.com and mail.yourdomain.com. |
preload | You request inclusion in the list built into browsers (hstspreload.org). Then even the very first visit is protected. This requires, among other things, one year, includeSubDomains and https on all subdomains. Getting removed again takes a long time. |
max-age=0 | Clears the remembered instruction again, on the next visit via https. |
includeSubDomains this also applies to every subdomain, such as an old camera or printer page without a certificate. So build it up: start with a short max-age, for example one day (86400), check that everything works, and increase it in steps to one year.HSTS at WebOké
On most of our servers, the web server sends HSTS by default for every website opened via https: Strict-Transport-Security: max-age=31536000. That's one year, without includeSubDomains and without preload. You don't need to do anything for that. On a few servers it's not on by default; there you turn it on yourself. Whether your site already sends HSTS, and with which max-age, you can see in your browser's developer tools or in the internet.nl test (see below).
The HSTS switch in DirectAdmin
In DirectAdmin go to Domains and click on your domain name. If Secure SSL is on, you'll see the switch Enable HTTP Strict Transport Security next to HSTS. Then click Save to save it. For step-by-step instructions, see Always sending visitors to https.
If your site already sends HSTS, you don't need the switch. Only turn it on if your site doesn't yet send HSTS. Set HSTS in one place: either in DirectAdmin or in .htaccess, not both. If the browser receives two HSTS headers, it only uses the first one.
The other security headers
| Header | Against | Value to start with |
|---|---|---|
X-Content-Type-Options | The browser starts guessing what a file is, which means an uploaded "image" could be executed as a script. | nosniff (the only valid value) |
Content-Security-Policy: frame-ancestors or X-Frame-Options | Clickjacking: another site loads your page invisibly in a frame, so visitors unknowingly click your buttons. | frame-ancestors 'self', or the older header X-Frame-Options: SAMEORIGIN |
Referrer-Policy | When clicking to another site, the browser sends along the address of the previous page, sometimes containing a search term or order number. | strict-origin-when-cross-origin, or the stricter same-origin or no-referrer. Internet.nl only counts the strict values as good. |
Permissions-Policy | Determines which browser functions your page and embedded frames may use, such as camera, microphone and location. | camera=(), microphone=(), geolocation=() turns those three off. If your site uses one of these functions, leave it out. |
Content-Security-Policy (full) | Restricts from which locations scripts, styles and images may load. This makes injected malicious scripts (XSS) much harder. | Custom per site. Avoid unsafe-inline, unsafe-eval and wildcards. |
Setting it up in .htaccess
Place these lines at the top of the .htaccess file in the public_html folder of your domain. First make a copy of the file.
<IfModule mod_headers.c> Header always set X-Content-Type-Options "nosniff" Header always set Content-Security-Policy "frame-ancestors 'self'" Header always set Referrer-Policy "strict-origin-when-cross-origin" Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()" # Only if your site doesn't yet send HSTS (see above): # Header always set Strict-Transport-Security "max-age=31536000" </IfModule>
Only add includeSubDomains once you're certain every subdomain has https. If you use WordPress, you can also set the headers with a plugin. Choose a well-known plugin that's still being updated, and set each header in one place: either in the plugin or in .htaccess.
Content-Security-Policy-Report-Only header. That blocks nothing, but shows in the browser's console what would have been blocked. If in doubt, leave this to your web developer, or start with just frame-ancestors.Testing
- Internet.nl: enter your domain in the website test at internet.nl.
- Your browser: open your site, press F12 (or choose Inspect), go to the Network tab, reload the page and click on the first request. Under Response Headers you'll see everything your server sends.
- The command line:
curl -sI https://yourdomain.com/shows the headers.
After every change, check that your site, forms and payment page still work. Also keep your CMS, plugins and themes updated. See Securing wp-login.php of WordPress.
Further reading
- MDN: Strict-Transport-Security (English)
- MDN: Content-Security-Policy (English)
- MDN: Permissions-Policy (English)
- HSTS preload list (English)
