Last updated March 3, 2019

When I need to build a blog or static site, my go-to is JAMstack with Jekyll and Netlify.

I also try and build out secure websites. I used to think it really only mattered on sites that process personal or sensitive information. But then I listened to episode 250 of the Shop Talk Show. April King and Alex Sexton talked about the different ways even static sites can get hijacked, serve up malicious content, and otherwise make the internet a bad place.

After listening to that podcast, I started using the Mozilla Observatory to audit all my projects and improve my security. In most cases, fixing security vulnerabilities was straightforward, but I have consistently had issues configuring the proper security headers with Jekyll and Netlify.

Here's how I finally got an A+ for my Jekyll/Netlify site on Observatory.

Step 1: Create a new Jekyll project

In a terminal, run jekyll new secure-jekyll-site. I've named this project secure-jekyll-site, you can choose any project name you wish.

Change into the secure Jekyll directory by running cd secure-jekyll-site.

Initialize the git repository with git init.

Make the initial git commit with:

git add .
git commit -m "init commit"

Upload the repo to GitHub.

Step 2: Deploy to Netlify

Follow the instructions to deploy a Jekyll site to Netlify.

Netlify's tools are beyond the scope of this post, but they are excellent. If you're into JAMstack development, I'd recommend getting familiar with them.

For now, let's continue by securing our initial deploy. The goal is to start off with the most restrictive set of policies possible. What you should find as you build out your site on top of this boilerplate is certain assets (images, styles, scripts) won't load as expected. That's the goal. You'll have to learn to modify your headers and security policies to allow these assets and other functionality to your site. This practice will put you in a position to make intentional choices about which policies to relax.

Step 3: Check the initial installation with Observatory

Visit the Mozilla Observatory and enter your Netlify domain in the form. You'll likely find the scan comes back with a grade of D+.

In the top right hand corner, in the box labeled Recommendation, you'll find Mozilla provides easy-to-read and helpful advice to improve your security headers. This blog post won't cover those in depth. Mozilla will prompt you to make one change at a time and re-scan your site. If you want to get a good feel for the process, I'd recommend going step by step.

The trick I found was figuring out how to deploy these changes to the live site. For the life of me, I couldn't figure out what I was missing. The rest of this blog should clarify the process of updating and improving your headers for the Mozilla Observatory security check.

Step 4: Add a headers file and include it in your Jekyll build process

In order to provide Netlify with custom headers, you need to add a _headers file to your project, as outlined in the Netlify docs. Add this file in the root of your Jekyll project.

However, if you were to run jekyll build or deploy Netlify right now - the _headers file wouldn't be available. Jekyll isn't set up to add it to the compiled _site folder yet.

To configure Jekyll to add the _headers file to your build process, use the include option. Inside your _config.yml file, add include: ["_headers"] to _config.yml.

Now when you build your Jekyll site, _headers will appear in the site root on Netlify. you can add the appropriate fixes as instructed by Mozilla.

Step 5: Add restrictive headers to your site

Now that _headers works on your Netlify deploy, you can add the appropriate headers to get an A+. Again, I'd recommend you make these changes step by step, following along with Mozilla to understand each piece of the process and make your own informed decisions. These are the headers that provide an A+ on the boilerplate Jekyll project:

    Content-Security-Policy: base-uri 'self'; default-src 'none'; form-action 'self'; frame-ancestors 'self'; img-src 'self'; script-src 'self'; style-src 'self'
    Referrer-Policy: same-origin
    X-Content-Type-Options: nosniff
    X-Frame-Options: DENY
    X-XSS-Protection: 1; mode=block


With these headers, a blank Jekyll site should score an A+ on Mozilla Observatory with a score of 120/100. It will also clear all the Content Security Policy checks they run.

It will be as restrictive as possible, so you may find you need to make adjustments to these headers to get certain tools and components to work on your site. Again, it's good practice to make the necessary changes intentionally, vs. trying to fix broken security policies retroactively.

Next steps

As I mentioned, some components may not work properly with these restrictive settings. The first you'll notice is the icon set provided by the minima theme. In the footer, you'll notice there ought to be a GitHub and Twitter icon. Those won't load with these settings, but changing default-src in the content security policy to 'self' should do so. Consider reading about default-src at the MDN docs before making this change, yourself. I won't be making it, to keep this boilerplate project as restrictive as possible.

If you'd like to use this boilerplate for your own Jekyll projects, you can fork the Secure Jekyll Site repo. You can also see the live site here.

All the Observatory scores listed here are current as of March 3, 2019