Adding HTTP headers to a Netlify static site

27 Jun 2019

Recently I have been using the Mozilla Observatory to do some security hunting on a community site I work on ( It’s a really interesting tool, but it might make you sad when you first check your baby out.

d+ rating


Well, I had some work ahead of me. As it turns out, most of the issues we had were around the HTTP headers that the site was sending. Given that this site is deployed as a set of static HTML files (produced via Jekyll) on Netlify, I wasn’t sure that there was much hope that we would be able to modify the headers.

One available option in this scenario is to use the HTML META tag to inject a header value from the HTML source. Unfortunately, it appears that this is only really useful for the Content Security Policy header, and even then some browsers may ignore the tag.

Additionally, the META tag can cause other issues like not being able to test the site locally very easily. For example, if I specify in the tag that all content must be served from a secure socket connect, then when I test locally I will need to have a way to use an https connection on my development machine. Not an impossible task, but it doesn’t help new people who want to contribute to the project.

Thankfully, after some searching around I found this article about headers and basic authentication in the Netlify docs. Basically, it says that you can instruct the Netlify servers to send specific headers by adding a file to the root of your site deployment named _headers. With this info I was off to the races.

After a good 2 days of reading, hacking, debugging, and repeating I was finally happy with the headers I had crafted for the site. You can see the final result here:


  Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; frame-ancestors 'none'; style-src 'self' 'unsafe-inline'; frame-src 'self'; img-src 'self'
  X-Frame-Options: DENY
  X-Content-Type-Options: nosniff
  X-XSS-Protection: 1; mode=block

  Content-Security-Policy: default-src https: 'unsafe-eval' 'unsafe-inline'

  Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; frame-ancestors 'none'; style-src 'self' 'unsafe-inline'; frame-src 'self'; img-src 'self'

pretty crazy huh? I’m not going to explain them all, you can find most of the advice given by the Observatory on Mozilla’s page about web security. The big win though was the final result:

a+ rating


At this point I was pretty happy and decided to put down my editor and vcs. I think there are one or two more tweaks that could be added to the site, but I am happy for now.