Skip to main content Accessibility Feedback

Securing serverless functions with CloudFlare Workers

Yesterday, we looked at how to create a simple serverless function with CloudFlare Workers and vanilla JS.

Today, we’re going to learn how to secure the API endpoint.

Using CORS headers

In yesterday’s article, our handleRequest() function looked like this.

/**
 * Respond to the request
 * @param {Request} request
 */
async function handleRequest(request) {

	let headers = new Headers({
		'Access-Control-Allow-Origin': '*',
		'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
		'Access-Control-Allow-Headers': '*'
	});

	return new Response(JSON.stringify({
		greeting: 'Hi there, friend!'
	}), {
		status: 200,
		headers: headers
	});

};

In the headers object, we can restrict calls to this API so a specific URL, or origin, using the Access-Control-Allow-Origin property.

For example, if I only wanted to allow API calls from gomakethings.com, I could do this.

/**
 * Respond to the request
 * @param {Request} request
 */
async function handleRequest(request) {

	let headers = new Headers({
		'Access-Control-Allow-Origin': 'https://gomakethings.com',
		'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
		'Access-Control-Allow-Headers': '*'
	});

	return new Response(JSON.stringify({
		greeting: 'Hi there, friend!'
	}), {
		status: 200,
		headers: headers
	});

};

If you tried to make a fetch() request to the API from somewhere else (like vanillajsguides.com), the API would return an error.

That’s great if you’re restricting to calls to a single domain, but what about multiple domains?

An allowed domains array

Let’s leave Access-Control-Allow-Origin with a value of *, and instead create an array of allowed origins.

// Allowed domain origins
var allowed = ['https://gomakethings.com', 'https://vanillajstoolkit.com'];

The handleRequest() method has an argument: request. This is the actual API request object.

It has a property, headers, that includes all of the headers that came with the API request. You can use the get() method, passing in origin as an argument, to get the request origin.

request.headers.get('origin');

We can use the Array.includes() method to check if the origin is in our array of allowed domains. If not, we’ll return a 403 error. Otherwise, we’ll return the actual response.

// Allowed domain origins
var allowed = ['https://gomakethings.com', 'https://vanillajstoolkit.com'];

/**
 * Respond to the request
 * @param {Request} request
 */
async function handleRequest(request) {

	let headers = new Headers({
		'Access-Control-Allow-Origin': '*',
		'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
		'Access-Control-Allow-Headers': '*'
	});

	// If domain is not allowed, return error code
	if (!allowed.includes(request.headers.get('origin'))) {
		return new Response('Not allowed', {
			status: 403,
			headers: headers
		});
	}

	return new Response(JSON.stringify({
		greeting: 'Hi there, friend!'
	}), {
		status: 200,
		headers: headers
	});

};

Try it yourself

Try running this snippet anywhere on gomakethings.com or vanillajstoolkit.com. Then trying running it somewhere else (like vanillajsguides.com).

(Open up developer tools, click on the Console tab, and copy/paste/return.)

fetch('https://falling-scene-37ce.gomakethings.workers.dev').then(function (response) {
	if (response.ok) {
		return response.json();
	};
	return Promise.reject(response);
}).then(function (data) {
	console.log(data);
}).catch(function (error) {
	console.warn(error);
});

Neat, huh?

Next week, we’ll cover more serverless topics, including using environment variables and creating a middleman API.