Security Headers
Whether you have a fully static site without any javascript and/or API usage or a fully tricked out JAMstack site, you will want to have security inplace to protect your visitors and your reputation.
What headers to add
Here are some of the recommended headers you should consider adding.
Content-Security-Policy
With CSP you are in control of what is executed your pages. Ranging from scripts, css, images and their origins/sources.Strict-Transport-Security
Or HSTS. This informs browsers that your site should only be accessed using HTTPS. This is important even for a fully static site as it can prevent your page being defaced by anyone able to perform a man in the middle attack.X-Xss-Protection
Cross-site scripting protection. An older header that is replaced by the CSP header, still it can be useful to protect those on older browsers.X-Frame-Options
Controls if your site can be included in an iframe. It can prevent click-jacking, ie tricking your users into clicking on scams or other malicious code pretending to be from you.X-Content-Type-Options
Prevents mime type sniffing and can prevent the execution of normally non-executable content types like images.Referrer-Policy
Controls the amount of referrer information is sent with navigations (on and offsite). Thus protecting your visitor’s privacy.Permissions-Policy
A newer header that controls the features (like camera usage) your website can request.
What settings to choose
Lets look closer at the recommended settings and see how we can configure them.
- Content-Security-Policy
I’ll admit this probably the most tedious one to setup and maintain. I’ve often added a feature and missed the CSP header, wondering why something didn’t work. Usually the dev tools in a modern browser will help you. To be the most secure, be sure to be very specific in what you allow. So while a default-src *
might be tempting, it is as insecure as not having the header at all.
Some examples would be to set it up like this:
default-src 'self' upgrade-insecure-requests
if all you have is self-hosted
default-src 'self'; connect-src 'self' https://api.external.io; upgrade-insecure-requests
to allow you to connect to an external api from javascript
default-src 'self'; script-src 'self' https://external.script.source; upgrade-insecure-requests
to allow loading scripts from some external.script.source.
default-src 'self'; style-src 'self' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; upgrade-insecure-requests
to allow for google fonts to load with CSP
MSD web docs has a long list of available options for you to setup.
- Strict-Transport-Security
Not much to this one, it is recommended to set it to a year, so max-age=31536000
should have you covered. If you want to include all your subdomains automatically.
More info on how it protects you can be found on the qualys blog.
- X-Xss-Protection
Again not much to this one, just turn it on and make sure it blocks it detects 1; mode=block
. It is only for older browsers.
- X-Frame-Options
I have this one set to SAMEORIGIN
so I can embed it myself if I wanted to. However it is very unlikely so DENY
is probably the better default.
- X-Content-Type-Options
Just set it to nosniff
and it should prevent your browser from trying to dynamically determine the mime type and avoiding some code execution vulnerabilities. More background on that in this MSD topic.
- Referrer-Policy
The default is probably what you’re looking for strict-origin-when-cross-origin
if you don’t use it on your own site either you can limit it even further. I tend to go for strict-origin
myself, rarely need the path or more anyway.
- Permissions-Policy
This is a newer one and could take some time to configure if you’re building an app. I’ve just restricted all features. You can play around with this generator to see whats possible. It allows you to control whether your pages can use some of the new apis like cameras, geolocation, battery, etc.
Where to set them
Now that we figured out how to configure everything we need to find out where we can set those headers so they’re actually useful. This can be somewhat of a journey depending on how you’re hosting your static site. Any webserver will have extensive documentation on how your headers can be configured, however some of the free/cheap hosting solutions may have more obscure method to configure these.
Cloudflare
For a long time you had to add a worker to your sites to have cloudflare server the security headers. However recently they added it as a featuer on their pages. Simply add a _headers
file (yes thats the exact filename) at the root of your site and cloudflare will do the heavy lifting. You can find the documentation here
Google buckets
GCP allows you to add headers through their load balancers, this may however cost quite a bit extra depending on your setup.
Netlify
Similar to cloudflare (or the other way around ;)) Netlify offers the ability to specify custom headers in a _headers
file at the root of your site. See their docs for more info.
Meta tags as alternative
If you happen to be stuck some of the headers mentioned above can also be added to your meta tags in a html page. While not quite as effective, it might be better than not having them. Keep in mind that not all features or headers are supported in this way. So if you do have the ability to specify them as headers that is recommended.
Here is an example:
<meta http-equiv="Content-Security-Policy" content="default-src 'self'">
<meta name=”referrer” content=”strict-origin-when-cross-origin” />