Making the website secure by adding additional HTTP headers in Netlify (and Zeit Now)
Tags: security, netflify, now, csp
Published: 2019-08-12 10:57:39

---
title: Making the website secure by adding additional HTTP headers in Netlify (and Zeit Now)
published: true
description: Brief explanation on how to add custom HTTP headers to Netlify to achieve `A+` score in https://securityheaders.com/.
tags:  Security, Netflify, Now, CSP 
series: Building jvega.dev
---

When I started thinking about building my new website, I wanted to make sure that even though its content is not sensible or private, I wanted to make sure it follows the best-in-class security practices.

In this first iteration, I didn't want to bother also managing the back-end infrastructure, so I decided to host the website in a third-party hosting provider. There are excellent options out there, but I decided to stick with [Netlify](https://www.netlify.com/) for the time being, I am not using any custom feature so it would be straightforward to migrate between services.

My goal is to get the top score in the [Security Headers](https://securityheaders.com/). If you have never used this website, it is brilliant; it gives you a score and recommendations on how to improve it.

This is the score you get with the default configuration in Netlify:

![](https://thepracticaldev.s3.amazonaws.com/i/85svu7q28or0276xft6n.png)

Not great, isn't it? But the site informs you about what is missing and give you links to understand more:

![](https://thepracticaldev.s3.amazonaws.com/i/5kfx8ut2jb4sj52r6ema.png)

The cool thing about Netlify is that allows you to set your custom HTTP headers effortlessly. You need to add a file named `_headers` with the headers.

> Make sure the file ends up in your built folder if you use Webpack or any build tool.

This is the content of the `_headers` file for an **A+** score:

```
/*
  Content-Security-Policy: default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self';
  Feature-Policy: accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; camera 'none'; encrypted-media 'none'; fullscreen 'self'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none';  picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none';
  X-Frame-Options: DENY
  X-XSS-Protection: 1; mode=block
  Referrer-Policy: no-referrer
  X-Content-Type-Options: nosniff

```

And this is how I got an **A+** score:

![](https://thepracticaldev.s3.amazonaws.com/i/ynrhdrxvztqyrg1qcf0g.png)

I will explain how did I come up with these values:

- `X-Content-Type-Options: nosniff` should always be set with that value.
- `X-XSS-Protection: 1; mode=block` this one is to provide XSS protection in older browsers that do not support the *Content Security Policy (CSP)* standard.
- `X-Frame-Options: DENY` this one is also added to protect browsers that do not support the CSP standard. The value should always be set to ` DENY` unless you are planning to show your site in an iframe on another website. This is very important to protect against `Clickjack attacks`.

The `CSP` value requires more explanation. I am not going to explain the whole CSP standard; I will focus on how do I approach setting the value.

I always start with denying any resource loading by default with:

```
Content-Security-Policy: default-src 'none';
```

And then I start adding only the necessary exceptions for the site to work. This approach is the most secure way as you don't need to fully know all the current directives available (CSP is a living standard; new directives are added regularly).

In the first iterations of this website, I only needed to be able to load scripts and images, so I add an exception for both to allow loading them *only* from the same domain. This means all content comes from my server and should be secure:

```
  Content-Security-Policy: default-src 'none'; script-src 'self'; img-src 'self';
```

As the development of the website evolves, I will continue to add more directives. For example, at some point, I will have to allow loading styles, so I will have to change the policy to be like:

```
 Content-Security-Policy: default-src 'none'; script-src 'self'; img-src 'self'; style-src 'self';
```
> **EDIT:** There is an issue with prefetch requests. As part of CSP v3, a new directive will be introduced to define their behavior `prefetch-src` but it is not available in any browser, yet. But if you set `default-src ` to `none`, prefetch queries are all blocked by default. Why? :(. So that gives me two options only: or don't use prefetch at all, or set the default to `self`... And I guess is not that bad as setting default to `self` is okay, but what if I have to make a prefetch to another domain, do I need to whitelist all requests to that domain? 

For the `Feature-Policy` I followed the same approach: I disallow all the features by default, and I will start enabling them as I need them. Unfortunately, the current standard does not support denying all by default hence the length of the value.

And last but not least, I disabled the referred information `Referrer-Policy` as I do not need it for now. I might enable it later if I need it for Analytics purposes. 

**Bonus: How to add custom headers to Zeit Now.**

[Zeit Now](https://zeit.co/now) also supports adding custom HTTP headers. This is the content of the `now.json` file to achieve the same as in Netlify:

```json
{
  "version": 2,
  "name": "jvegadev",
  "routes": [
    {
      "src": "/.*",
      "headers": {
        "Content-Security-Policy": "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self';",
        "X-Frame-Options": "DENY",
        "X-XSS-Protection": "1; mode=block",
        "X-Content-Type-Options": "nosniff",
        "Referrer-Policy": "no-referrer",
        "Feature-Policy": "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; camera 'none'; encrypted-media 'none'; fullscreen 'self'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none'; payment 'none';  picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none';"
      }
    }
  ]
}
```

**Interesting links:**
- [Clickjack attack – the hidden threat right in front of you](https://www.troyhunt.com/clickjack-attack-hidden-threat-right-in/)
- [Hardening your HTTP response headers](https://scotthelme.co.uk/hardening-your-http-response-headers/#x-frame-options)
- [MDN CSP](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP)