In this blog post I will be showing you how to take your site security with Cloudflare workers, going from D to A on securityheaders.com quickly and easily.

The Problem

With a lot of my sites, they are hosted on platforms like Github/Gitlab pages. This comes with many advantages, like git instant deployment and free hosting and management of said sites. But, they lack the fine control you get when you are in charge of the webserver- like setting various security headers (amongst other things). However, enter Cloudflare workers- the perfect solution; the holy grail. So today, we are going to talk about Cloudflare workers, and how they can be used to solve this problem.

What are Cloudflare workers?

Cloudflare workers is a serverless solution, designed for relatively small applications that don't need to always be on or run for long (workers can only run for 10ms on the free plan). On the free plan, you're allowed up to 100,000 requests per day- plenty for most. It allows rapid scaling while saving costs, as the code you deploy will only run when it needs to, rather than the traditional model of renting/buying an always-on server and hosting the code there. With Cloudflare workers, there is also big performance improvements. Since they already have data centers to host their CDN content around the world, these servers also host their workers. This means the workers will be executed close to the user, reducing latency and therefore increasing performance. So let's use them to improve our site security without hindering our site's performance.

How can we use them to solve our problem?

To start, we need to set up our worker: Navigate to the Cloudflare workers section of the dashboard, and click create a worker. You should end up with a similar screen:

From here, it's very easy. Cloudflare has an example on how to add headers on their documentation site. We can take the example, and use it to add our security headers:

addEventListener('fetch', event => { event.respondWith(handleRequest(event.request)) }) async function handleRequest(request) { let response = await fetch(request) // Make the headers mutable by re-constructing the Response. response = new Response(response.body, response) response.headers.set('X-Frame-Options', 'SAMEORIGIN') response.headers.set('Referrer-Policy', 'same-origin') response.headers.set('X-XSS-Protection', '1; mode=block') response.headers.set('Report-To', '{"group":"default","max_age":31536000,"endpoints":[{"url":"https://danielgray.report-uri.com/a/d/g"}],"include_subdomains":true}') response.headers.set("Feature-Policy", "accelerometer 'none'; camera 'none'; geolocation '*'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none'") return response }

Let's break this down, step by step:

The first header, X-Frame-Options controls who can embed our site in an iframe . In this case, it is set to ' SAMEORIGIN' , which only allows a site that has the same domain to embed the site in an iframe. So for example, if example.com tried to load site.com in an iframe, and site.com used this header- example.com would not be allowed to do this. This prevents clickjacking attacks , which prevents an attacker using some sneaky CSS to deceive a user to clicking a button that actually activates an important function on your site.

controls who can embed our site in an . In this case, it is set to ' , which only allows a site that has the same domain to embed the site in an iframe. So for example, if example.com tried to load site.com in an iframe, and site.com used this header- example.com would not be allowed to do this. This prevents , which prevents an attacker using some sneaky CSS to deceive a user to clicking a button that actually activates an important function on your site. Next, we have the Referrer-Policy , which controls when the referrer header is sent. Setting it to 'same-origin' means that the header will only be sent when navigating between pages on the same site. This comes with a big privacy advantage- as if users navigate from your site back to their search engine (for example), the search engine will not know where the user has come from. This helps prevent tracking of your site users, especially handy if the site you host has sensitive (or embarrassing) content.

, which controls when the header is sent. Setting it to means that the header will only be sent when navigating between pages on the same site. This comes with a big privacy advantage- as if users navigate from your site back to their search engine (for example), the search engine will know where the user has come from. This helps prevent tracking of your site users, especially handy if the site you host has sensitive (or embarrassing) content. Pushing on, we have the X-XSS-Protection header, which controls the use of cross-site scripting . Although it can be replaced by a content security policy, since we are not deploying one today it is important to use this header instead. Setting it to '1; mode=block' outright blocks cross-site scripting attacks. This attack vector was once often exploited, but now these such attacks are no so common.

header, which controls the use of . Although it can be replaced by a content security policy, since we are not deploying one today it is important to use this header instead. Setting it to outright blocks cross-site scripting attacks. This attack vector was once often exploited, but now these such attacks are no so common. The next header we have is the Report-To header, which is a very new header. It is not so much a security or privacy improvement but sends reports about issues or potential issues with your site- vital to fix real-world problems and improve the experience for your users. Check out Report URI to enable this for your site.

header, which is a very new header. It is not so much a security or privacy improvement but sends reports about issues or potential issues with your site- vital to fix real-world problems and improve the experience for your users. Check out Report URI to enable this for your site. Finally, we have the Feature-Policy header, another pretty new header which controls what features the browser is allowed to use. Setting it to accelerometer 'none'; camera 'none'; geolocation '*'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; payment 'none'; usb 'none' means the only feature our site will be allowed to use is geolocation. This helps prevent attackers from injecting content into your site and using features like the camera and microphone. Specifying what features you want to use improves the security for users, and increases trust- users know what features you plan on using.

Now, let's talk about deploying this to our site.

Deployment

To bring our Cloudflare worker to life, all we have to do is press 'Save and Deploy'. This means our worker is now deployed and ready to use, but we still need to set up our 'routes', which will apply the headers to our site. Head to the site you want to enable the headers for, and click 'workers'. Now click 'add route'. You should see this:

Here, we need to add 2 routes. One will cover the base domain and associated paths, the other will cover our subdomains. To do this, you need to enter the following into the 'route' input (you need to do this separately for each): sitename.com/* and *.sitename.com/* . Then select the name of the worker in the dialogue, hit save and...

We're done! Head to securityheaders.com to test your site. You should see a shiny A badge on the result page!

Now, this isn't perfect, we could have a content security policy- but this requires more work; something for another day. But we have successfully improved the security of our site!

So today we have seen how we can easily use Cloudflare workers to quickly improve the security of our sites. This will work for all our Cloudflare sites and allows us to add headers to our site, even if we do not control the webserver- like Github pages for example. As always, if you have any questions, feel free to contact me via the methods listed on my website. See you next time!