Security HTTP Headers - Is your website secure?

Everyone wants to be secure on the web, but security is hard. There are many ways one can exploit a website but what if we told you just by setting a few HTTP Headers, you can reduce your attack surface area?

Scott Helme created to help drive up the usage of security-based headers across the web. The tool scans a website, then displays a report with a rating from F to A+, informing you of extra security headers you could add to increase your site's security, what that header does and how it can protect you.

Let’s take a deep dive into the security headers we have set on

Strict Transport Security

Strict Transport Security asks for the site to be loaded over HTTPS instead of HTTP so when the site is loaded again, your browser remembers and automatically connects over HTTPS. The only annoyance is the initial request can come over HTTP before the browser sees the header.

The header accepts the following values:

  • max-age=<expire-time> -- time in seconds

  • includeSubDomains (optional) -- applies the rule to subdomains

  • preload (optional) -- stated intent for being added to the HSTS preload list

We set the value of this header to:

max-age=31536000; includeSubdomains; preload

31536000 seconds being 1 year.

X Content Type Options

X Content Type Options indicates that MIME types e.g. text/html, application/javascript should not be changed or followed. The header only accepts the value:

x-content-type-options: nosniff

so that's what we set it as.

X Frame Options

Frames/ iframes are a tool that can improve user experience, e.g. PayPal opening a frame to allow for payment. They can also be used for malicious intent e.g. clickjacking attacks. Frame Options specifies rules around whether the browser should be allowed to render pages in frames, iframes, embeds or objects.

It accepts the following values:

  • deny -- disallows all iframes

  • sameorigin -- only allows iframes from the same origin

We set this to:

x-frame-options: sameorigin

This reduces the attack surface area as now the attacker would have to inject their own persistent malicious iframe code into our website to attempt to pull off a clickjacking attack.

Referrer Policy

When navigating from one site to another, the Referrer Policy header, (originally called the referer header, yes, spelt incorrectly…), keeps track of where you have just been. Referrer Policy allows for control over this header via the scheme and origin e.g. HTTP or HTTPS.

It accepts the following values:

  • "" -- rules for referer header not set

  • no-referrer -- disallows site url to appear in the referer header at all

  • no-referrer-when-downgrade -- only allows site url in the referer header if navigated site is not over HTTP

  • same-origin -- only allows site url in the referer header if same origin

  • origin -- allows site url to appear in referer header, but strips away path information

  • strict-origin -- same as origin but only if navigated site is not over HTTP

  • origin-when-cross-origin -- same as origin but only when navigating to a non origin site

  • strict-origin-when-cross-origin -- same as origin-when-cross-origin but only if navigated site is not over HTTP

  • unsafe-url -- the full url is always set in the referer header

We went with the following to keep off of HTTP websites:

referrer-policy: no-referrer-when-downgrade

Permissions Policy

The Permissions Policy header, which is the new improved feature policy header, allows for control over specific permissions in your browser on your site e.g. autoplay, fullscreen, geolocation and microphone.

This header accepts the following format: permissions-policy: <permission>=(<control level>).

For <permission>, there is a vast array of values that can be configured, check out here to see the full list.

The <control level> parameter accepts the following values:

  • () -- disallows the feature

  • self -- feature allowed on the current page and in iframes on the origin

  • src (iframe only) -- feature allowed on in the current iframe, as long as it comes frame the same origin as website

  • [origin] -- a specified list of origins the feature is allowed on, space-separated

We set this to:

Permissions-Policy: Permissions-Policy: geolocation=(self), midi=(), payment=(), 
fullscreen=(self), gyroscope=(), magnetometer=(), camera=(), microphone=(), 
sync-xhr=(self), usb=(), picture-in-picture=(), autoplay=()

Content Security Policy

When you load up a website, a bunch of assets are loaded behind the scenes. If the source code was tampered with by a malicious third party, who's to say that blob of JavaScript that's getting pulled in isn't mining Bitcoin? Or stealing your data? The Content Security Policy header protects you against this. It allows you to specify with precision where content can be loaded, so if the source is tampered with, your browser will block loading content that isn't on the whitelist.

The header accepts the following format: <source> <control level>.

The parameter accepts a large list of values, some of which include:

  • default-src -- the fallback for non-specified sources

  • script-src -- policy for executable scripts (JS)

  • style-src -- policy for styling (CSS)

  • image-src -- policy for images

  • frame-src -- policy for iframes/ frames

The parameter allows the following values:

  • none -- blocks use of this source

  • self -- allows the use of the source for current origin only

  • unsafe-inline -- allows the use of inline CSS and JS

  • unsafe-eval -- allows use of eval() JS function

  • <url> -- allows source from an exact URL (supports * wildcard) e.g.*

The full list can be found here, along with more detailed information.

Warning! You do need to be careful when setting this header. As it will block assets being loaded, a mis-configured header value could very well break your website. Before setting the Content Security Policy, we recommend testing it out via setting the Content Security Policy Report Only header, this will log violations of your policy in the console of your browser instead of blocking assets. We decided to set the header as:

Content-Security-Policy-Report-Only: default-src https:; 
style-src 'unsafe-inline' https:; script-src 'unsafe-inline' https:

This policy will block any content loaded over HTTP, in the future I would like to also block inline JS and CSS, however, this is not possible at the current time.

We are currently trialling this header in report-only mode.


As you can see security headers are extremely powerful, easy to implement and have a huge payoff by removing the potential of certain exploits. Let's do a scan on our site and see the results.

Screenshot from 2021 03 22 14 05 22

Try it out on your own website and see how you score!