Mapping a Cloudflare Worker to a Subdomain

In this article, we will discuss a simple way for you to map a Cloudflare worker on a subdomain on your website.

Let’s say you have a website (example.com) from where you want to call an external API on https://some-api.com/path/to/resource?apikey=xyz.

Ordinarily, you will have to call this API on your backend by setting up a dedicated server. You will also need to create a wrapper API which you have to maintain.

That’s just extra cost and time investment for such a small task.

This is where the Cloudflare Workers come to the rescue. You can use these workers on your subdomain (api.example.com) or a dedicated path (example.com/api/path/to/resource) to run logic on Cloudflare’s servers without setting up a server or managing yet another service for you website/app.

A specific example.

Let’s suppose you want to call https://some-api.com/path/to/resource?apikey=xyz from your website example.com.

In your client-side js, you could do something like this.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const getApiResponse = async () => {
try {
const apikey = "xyz";
const res = await fetch(
`https://some-api.com/path/to/resource?apikey=${apikey}`
);
const { data } = await res.json();

return data;
} catch (error) {
console.error(error);
} finally {
console.log("done");
}
};

getApiResponse();

This, I must admit will work fine. But there is an issue with this approach.

  • You have to embed the apikey on your client.
  • In case the API is of your own backend (eg Firebase HTTPS Functions, AWS Lambda), then it is susceptible to a DDoS attack since the URL is exposed to the world.

Granted, not all applications will have these issues, but some of them in the wild definitely do suffer from these.

Also, I’d like to point out that lots of API services allow origin restrictions to protect unauthorized API usage. Case in point Google Maps. But, again, not all API providers have this feature.

Here, we can easily add Cloudflare Workers in the middle of your API request.

Initial Setup

  • Add your domain to Cloudflare. There is a very helpful tutorial on Cloudflare community for just that. You should follow this and then come back here.

https://community.cloudflare.com/t/step-1-adding-your-domain-to-cloudflare/64309

Creating a Worker

  • Go to the Workers tab in the Cloudflare Dashboard.
  • Click on the Manage Workers button to go to the Workers screen.
  • On the top-right button Create a Worker.
  • Click on Save and Deploy. Right now, leave the existing code intact. We will touch that later.
1
2
3
4
5
6
7
8
9
10
11
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});

/**
* Respond to the request
* @param {Request} request
*/
async function handleRequest(request) {
return new Response("hello world", { status: 200 });
}
  • Go back to the Workers screen again and click on the Add route button and add https://api.example.com/* in the Route and your new worker name in the Worker field.
  • Click Save.

Updating DNS

  • In the Cloudflare Dashboard, click on the DNS tab where you will see the DNS Management screen.
  • Click on the + Add Record button and add a CNAME DNS record with the following details
1
2
3
4
5
Type: CNAME
Name: api
IPv4 Address: api.example.workers.dev
TTL: Auto
Proxied: Yes

Once this is done, click Save

Your worker is now mapped to the api.example.com via the CNAME DNS record. Any request to api.example.com will now go to the worker.

From here, you can call the external API

Proxying Requests from Cloudflare Edge

This is the final part of your worker. Here’s the code.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
const apikey = "xyz";
const MAIN_API = "https://some-api.com";

addEventListener("fetch", (event) =>
event.respondWith(handleRequest(event.request))
);

/**
* @param {Request} request
*/
const handleRequest = async (req) => {
try {
const { url, method, headers } = req;
const { pathname, search } = new URL(url);
const body = await req.text();
const requestForwardingUrl = new URL(pathname, MAIN_API);
const params = new URLSearchParams(search);

// API_KEY is required in every request.
params.append("apikey", API_KEY)

// All other params from the example.com url will be added
// to the fetch request.
[...params.entries()].forEach(([value, key]) => {
requestForwardingUrl.searchParams.append(key, value);
});

return fetch(requestForwardingUrl.href, {
method,
headers,
body: method === "GET" ? undefined : body,
});
} catch (error) {
console.error(error);

return new Response(JSON.stringify({ message: "Something went wrong" }), {
status: 500,
});
}
};

Every request to the api.example.com is sent to Cloudflare DNS. The Cloudflare DNS then intercepts this request and check the CNAME linked to this domain.

A request to api.example.com/path/to/resource will be sent to https://some-api.com/path/to/resource.

In addition to this, the Worker will also add the apikey query param to the request sent to the some-api.com.