Help combining workers

Hello all,

I have the following workers code already in production:

let securityHeaders = {
	"Content-Security-Policy" : "upgrade-insecure-requests",
	"Strict-Transport-Security" : "max-age=2592000",
	"X-Xss-Protection" : "1; mode=block",
	"Permissions-Policy" : "accelerometer=(), camera=(), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(), payment=(self), usb=()",
	"X-Frame-Options" : "SAMEORIGIN",
	"X-Content-Type-Options" : "nosniff",
	"Referrer-Policy" : "strict-origin-when-cross-origin",
}

let sanitiseHeaders = {
	"Server" : "cloudflare",
}

let removeHeaders = [
	"Public-Key-Pins",
	"X-Powered-By",
	"X-AspNet-Version",
]

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

async function addHeaders(req) {
	let response = await fetch(req)
	let newHdrs = new Headers(response.headers)

	if (newHdrs.has("Content-Type") && !newHdrs.get("Content-Type").includes("text/html")) {
        return new Response(response.body , {
            status: response.status,
            statusText: response.statusText,
            headers: newHdrs
        })
	}

	Object.keys(securityHeaders).map(function(name, index) {
		newHdrs.set(name, securityHeaders[name]);
	})

	Object.keys(sanitiseHeaders).map(function(name, index) {
		newHdrs.set(name, sanitiseHeaders[name]);
	})

	removeHeaders.forEach(function(name){
		newHdrs.delete(name)
	})

	return new Response(response.body , {
		status: response.status,
		statusText: response.statusText,
		headers: newHdrs
	})
}

It works nicely and allows me to set some headers that cannot be served by our webservers, since CloudFlare serves their own. This is what introduced me to workers.

While we proxy our traffic through CloudFlare, nothing at the moment stops malicious actors from hitting our website directly. While we could whitelist CloudFlare IP addresses and deny everything else, there is a neat solution using Workers that I have found.

In htaccess, you look for a key in the headers.
In CloudFlare, you add this code to a worker:

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

/**
 * Send header to origin, allowing for
 * .htaccess to block requests
 * not coming from Cloudflare
 */

async function handleRequest(request) {
  // Make the headers mutable by re-constructing the Request.
  request = new Request(request)
  request.headers.set('EXAMPLE', 'abcdef0123456789')

  return await fetch(request)
}

Only traffic that comes through CloudFlare will have this secret key. That futureproofs any CloudFlare IP changes.

My problem is I don’t understand javascript or workers enough. I could use either of these in isolation, but I don’t know how to merge these two together. Simply appending the second set of code to the bottom of our existing worker did not work.

Anyone familiar enough with javascript/workers to help me?

Try this.

let securityHeaders = {
	"Content-Security-Policy" : "upgrade-insecure-requests",
	"Strict-Transport-Security" : "max-age=2592000",
	"X-Xss-Protection" : "1; mode=block",
	"Permissions-Policy" : "accelerometer=(), camera=(), geolocation=(self), gyroscope=(), magnetometer=(), microphone=(), payment=(self), usb=()",
	"X-Frame-Options" : "SAMEORIGIN",
	"X-Content-Type-Options" : "nosniff",
	"Referrer-Policy" : "strict-origin-when-cross-origin",
}

let sanitiseHeaders = {
	"Server" : "cloudflare",
}

let removeHeaders = [
	"Public-Key-Pins",
	"X-Powered-By",
	"X-AspNet-Version",
]

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

async function addHeaders(req) {
	req = new Request(req) //new code added here
	req.headers.set('EXAMPLE', 'abcdef0123456789') //new code added here
	let response = await fetch(req)
	let newHdrs = new Headers(response.headers)

	if (newHdrs.has("Content-Type") && !newHdrs.get("Content-Type").includes("text/html")) {
        return new Response(response.body , {
            status: response.status,
            statusText: response.statusText,
            headers: newHdrs
        })
	}

	Object.keys(securityHeaders).map(function(name, index) {
		newHdrs.set(name, securityHeaders[name]);
	})

	Object.keys(sanitiseHeaders).map(function(name, index) {
		newHdrs.set(name, sanitiseHeaders[name]);
	})

	removeHeaders.forEach(function(name){
		newHdrs.delete(name)
	})

	return new Response(response.body , {
		status: response.status,
		statusText: response.statusText,
		headers: newHdrs
	})
}
3 Likes

Thank you, that works perfectly!

1 Like

This topic was automatically closed 3 days after the last reply. New replies are no longer allowed.