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'; 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'; font-src 'self'; 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.


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.


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” />


Comments powered by