Adding a Content Security Policy (CSP) with Cloudflare Workers

Published: April 11, 2020

Tags:

I had been interested in adding a Content Security Policy (CSP) to this website for a while. However, the site is built with Jekyll and hosted on GitHub pages, which doesn’t allow you to set custom HTTP response headers such as Content-Security-Policy1. I did a bit of research and found it would be possible to add them through Cloudflare (which I use as a CDN / DNS provider) via their “Cloudflare Workers” feature. In this post I want to walk through the setup process.

Create the Worker

Visit the “Workers” tab within your Cloudflare account.

Screenshot of Workers tab in Cloudflare Dashboard

Click the “Manage Workers” button and then click “Create a Worker”

NOTE: Cloudflare will grant you 100,000 free worker requests per day

The code for the worker (which is based on the “Alter Headers” template) is as follows:

addEventListener('fetch', event => {
  event.respondWith(handleRequest(event.request))
})

async function handleRequest(request) {
  let response = await fetch(request)

  response = new Response(response.body, response)

  response.headers.set(
    "Content-Security-Policy",
    "<<POLICY GOES HERE>>"
  )

  return response
}

Replace «POLICY GOES HERE» with your Content Security Policy.

NOTE: This site uses Content-Security-Policy-Report-Only to run in report only mode.

Create A Route For Your Worker

Back on the Workers dashboard click the “Add Route” button.

For this site the route is configured as follows:

Screenshot of how to configure route

To give a quick summary:

  • Configure your route using Cloudflare’s Matching Behavior syntax
  • Select the Worker you just created
  • Expand the “Request limit failure mode” accordion to configure Failure mode (You probably want “Fail open) so that users can continue to use your website if you run out of requests

One other recommendation is to add additional routes to prevent the CSP worker from processing requests for static assets.

For this website I have the following routes configured:

Screenshot showing the list of routes configured for this site

Footnotes

1 . Yes, I’m aware there is also a meta tag (although I only recently realized that as an option)

Max Chadwick Hi, I'm Max!

I'm a software developer who mainly works in PHP, but loves dabbling in other languages like Go and Ruby. Technical topics that interest me are monitoring, security and performance. I'm also a stickler for good documentation and clear technical writing.

During the day I lead a team of developers and solve challenging technical problems at Rightpoint where I mainly work with the Magento platform. I've also spoken at a number of events.

In my spare time I blog about tech, work on open source and participate in bug bounty programs.

If you'd like to get in contact, you can find me on Twitter and LinkedIn.