Setting up a reverse proxy with CloudFlare workers
Published: 2023-09-08 23:21:09


## **What is a reverse proxy in the first place?**


In layman's terms, it is a middleman sitting between two parties to make transactions between them smoother, faster, and more secure. Think of a waiter in a busy restaurant. The job of a waiter is to take orders from you and pass them to the kitchen staff and when your order is ready, serve the food from the kitchen to your table. As you can tell, you really don’t have to do much at a restaurant. All the hard work of communication and serving the food is handled by the waiter, here the waiter is acting as a **middleman** or **reverse proxy** between you and the chef.


If we apply this analogy to the digital world, a reverse proxy is a **server** between your clients (browser, mobile app etc.) and **web servers**. It can intercept the requests from the client before it reaches the server. This in turn opens a lot of opportunities for improving web server’s characteristics. Just as the waiter in a restaurant takes orders and serves food. It also takes care of billing and payments, managing multiple customers at the same time, managing crow during rush hours etc. A reverse proxy server can also do similar tasks in the internet world like **balancing traffic** between multiple servers, mitigating **risky behaviors,** and **caching** the responses for the same queries.



![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y9c0vlvsroqxcps0omwp.png)


**But**, our goal for using a reverse proxy is different.  We want to perform redirects between our websites. In this case, I’ve three websites 


**a**. **Main landing page** which I want to point to `kanvasai.com` 


**b. Medium integration page** which I want to point to `kanvasai.com/medium` followed by my app’s query parameters like `_?utm_source=&page=`_


**C. Dev. to integration page** again kanvasai.com/devto` followed by my app’s query parameters like _`?utm_source=&page=`_


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/2499291f-1cce-4a69-87cc-5af389b0d57f)


For the sake of brevity all there websites are hosted on [webflow.io](http://webflow.io/) and these are records on my GoDaddy DNS.


![DNS records, Godaddy.com](https://blog-images-prod.s3.amazonaws.com/fae7add9-93fc-4fe0-915d-c50d20fd8dde)


Notice, that I created three records (main, medium, devto) with CNAME pointing to a non-ssl server of [webflow](http://webflow.io/) (Usually, we use `proxy-ssl.webflow.com). I'll explain later why.


This gives me three websites with **three** **different** **subdomains** under one domain. 


> 💡 _However, an interesting thing to note here is all the web crawlers and robots treat all these three websites as a different entity hence, this hurts the SEO significantly and results in a ranking drop._

- [main.kanvasai.com](http://main.kanvasai.com/)
- [medium.kanvasai.com](http://medium.kanvasai.com/)
- [devto.kanvasai.com](http://devto.kanvasai.com/)

So, how do we fix it such that when any crawler/user tries to access the

- [**main.kanvasai.com**](http://main.kanvasai.com/) gets redirected to **kanvasai.com/**
- [**medium.kanvasai.com**](http://medium.kanvasai.com/) gets redirected to **kanvasai.com/medium,** and
- [**devto.kanvasai.com**](http://devto.kanvasai.com/) gets redirected to **kanvasai.com/devto**

**Let’s find out how CloudFlare workers can help**


_Workers are just javascript code running in Chrome browser environment. The good thing is that it runs closer to your user's browsers. The bad thing is that you cannot use node packages like in Serverless functions but all modern browsers APIs are supported. Read more about them_ [_here_](https://developers.cloudflare.com/workers/learning/how-workers-works/)_._


> _**Disclaimer : This article assumes you know little bit about cloudflare workers and have tried building with it. So, basic stuff will be excluded and maybe hyperlinked to other blogs.**_


> _**Note : If you’re going to apply this technique on production apps, please note that every `CNAME` record will be intercepted by worker, hence needs to be handled with correct worker logic for every route.**_


### Prerequisite :

- working CloudFlare account (free tier also works for this tutorial),
- access to your domain registrar (in this case I’m using Godaddy but the process is the same for every other DNS providers),
- Any code editor that supports JS/TS language features(I will use VS code),

### **Step 1 : Create a worker project with Cloudflare cli**


Create a new worker project by running the following command in the terminal. `website-stitcher` is the project name


```bash
npm create cloudflare@2 -- website-stitcher
```


After running this command, CLI will create a directory named `web-stitcher` and you’ll be prompted through a series of questions,

1. choose **hello world** worker

![Untitled.png](https://blog-images-prod.s3.amazonaws.com/80c3b5c3-edfd-4ab5-8d3a-4d48a454169a)


b. Always say **yes** to typescript


![Screenshot_2023-08-16_at_8.44.30_PM.png](https://blog-images-prod.s3.amazonaws.com/d36b2cdb-4d4c-47e4-9b4a-1a88bc92d78d)


c. git version control (you can do this later also)


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/9ff9b788-3cc1-4abc-8059-1ef71c043ff5)


d. Yes, deploy our sample worker.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/1a14f08d-85ac-4311-b8eb-d060d692b14e)


After selecting ‘yes’, Cloudflare cli will try to log in to your Cloudflare account via browser. Authorize the authentication of cli with the account.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/6497e21d-66d0-4892-a23c-9a9459a59d44)


and Voila! we have our Cloudflare worker ready and deployed. (A browser window should open up automatically with the message ‘hello world’)


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/e8e5dba7-67ef-49e1-8721-646d452fce82)


Now, we have a Cloudflare worker boilerplate code ready. Open it in your editor of choice. We don’t need to worry about all the files now. We’ll write all our logic in `src/worker.ts` file


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/0f1b65e7-fb07-4153-bb17-8fb1c829e544)


### **Step 2: Write the logic to handle incoming requests and redirect its response**


The fetch function has access to the request object which we will use to identify domains, subdomains, pathname, search params, etc. using the below snippet.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/e37fe5fb-cc71-4964-9353-12003f9477b0)


Let’s define our constants **ROOT_DOMAIN**, **SUBDOMAINS**, **MAIN_SUBDOMAIN** and main **WEBSITE_URL.**


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/31efa7cb-7dcc-492f-aa68-f58fb20a9aab)


Now, we need to write js/ts code to check the incoming hostname for subdomains and redirect it to sub- route. Something like this,


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/81016548-f1c7-4850-a7be-45be059f29cc)


By looking at the test cases we can break down the logic into three main blocks

- if the **hostname** is not the same as the **ROOT_DOMAIN**
	- here, we need to check SUBDOMAINS if they match the hostname’s subdomain. If it matches one of the SUBDOMAINS we need to redirect the user to the right url.
	- we should also check if it matches our MAIN_SUBDOMAIN and redirect the user to the main website
	- If does not meet the above two conditions then just return data from the original request URL.

	
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ui28kemewx2fr8umyje7.png)

- if the **pathname** matches one of the **SUBDOMAINS** and the **hostname** is the same as **ROOT_DOMAIN**
	- here, we just need to fetch the data from the matching subdomain and redirect the user to the correct URL.

		
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w6mi8toi0xo201d2rvoe.png)

	- or if there’s no subdomain or subroute, fetch data from **MAIN_DOMAIN** and return a response or redirect the user.


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ng23cybcgrgvjayq5qgl.png)
		
- If the pathname has a trailing `slash` (important case)
	- SEO crawlers treat `kanvasai.com/medium and [`kanvasi.com/medium/`](http://kanvasi.com/medium/) as separate routes. Hence, we need to take care of the `trailing slash`. It’s easy with a bunch of js string methods.

		
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kk2u2pyjgjcscq80vtwf.png)


It took some time and hit and trail to come up with logic and could be a little overwhelming to understand at once. Hence, I’m attaching a flow diagram to visualize the conditions.


If you prefer you can just copy and paste the same code change values of constants ROOT_DOMAIN, SUBDOMAINS, MAIN_SUBDOMAIN, and main MAIN_URL to your records.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/a496359c-4e84-46c5-b4f5-6c3e9c4c8dba)


The complete code looks like this


[embed](https://gist.github.com/fullstackNRJ/7c8a1a12612749f04dc08c6403fe4789)


Run the following command to deploy the code/worker


```bash
npx wrangler deploy
```


Head over to the Cloudflare _**dashboard > workers & pages**_. You should see list of all the workers here. I have multiple workers but if it’s a new account you should see just one worker.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/2ca88147-9621-457d-ae81-c1b0da3562eb)


Navigate inside your worker by clicking on it. You can see all relevant information related to your worker. There are tabs like triggers, logs, deployments, etc.


Next, we need to define the trigger for this worker. Right now, it is deployed to some random URL by Cloudflare.


### **Step 3: Register your site with Cloudflare zone**


Before adding a trigger for our worker we need to delegate our DNS nameserver to the Cloudflare. (If your DNS registrar is Cloudflare you don’t need to do this step).


On the main dashboard, click `Add Site` button on the top right corner, and enter your domain name ex. kanvasai.com. 


Now, follow the steps shown by Cloudflare for moving nameservers. This will allow Cloudflare to fetch all the existing DNS records. But, please make sure you have all the records present. 


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/b60e733c-02de-478d-b945-ba4d94b16949)


Adding [`albert.ns.clouflare.com`](http://albert.ns.clouflare.com/) and [`annabel.ns.cloudflare.com`](http://annabel.ns.cloudflare.com/) to my goDaddy domain DNS setting page.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/fa17348b-8653-4ab1-9bfc-a94743d15ae0)


Finish the site setup by clicking continue and check the DNS nameserver status. This may take some time to verify new nameservers. _**Important, thing to note here is now you have to manage all your DNS records from your Cloudflare account. Your DNS registrar will not have control over your records.’**_


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/cc90a152-a517-47c8-ada1-5c81de5b78ca)


> _**Note**_ _: Make sure you have all the records available in the cloudflare DNS records too. In my case, my two records were not transferred so I had to add them again manually. This can cause_ _**downtime**_ _to your app so be careful while doing this transfer._


### **Step 4: Add your root domain as the worker’s trigger**


Now, we are ready to add triggers to our workers. Navigate to your worker by clicking workers & pages on the left panel and then clicking on your worker from the list.


On this page, click on the `Triggers` tab next to `Metrics.`  Here, click on the **Add Custom Domain** button and add your domain.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/4796a68e-0206-40f0-a7f4-1e7a5928d392)


Note: you might see this error if you did not follow the last step correctly. In order to add your custom domain as a trigger for workers, your custom domain must be delegated to Cloudflare nameservers.(Don’t worry, your registrar will stay the same, GoDaddy in this case).


Cloudflare adds a new DNS record to type worker, you can check this to verify if your custom domain is set up correctly.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/020f453f-cfbf-4dfe-bd6b-e15babd98ac4)


### **Step 5: Attach a wildcard route to your worker**


Now, for our worker logic to run correctly we need to forward all the requests from all the sub domains to our worker. So, we’ll add a route for this we are going to use a wildcard (_) to define a pattern of URLs that are allowed or that trigger our worker. So,_ just below the custom domain, there is a section for routes (also called worker routes).  I’ll add `*.kanvasai.com/*`  as the route and choose [`kanvasi.com`](http://kanvasi.com/) as the zone. This represents all the subdomains and subroutes that have [kanvasi.com](http://kanvasi.com/) as the root domain.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/371ded2c-8662-4d60-98bd-24655ae1c1c4)


we should see this message once the route is properly configured.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/e1c0209a-7e20-4772-af08-0b9bb34e1ec4)


Now, we can already see the worker in action. Try visiting your root domain and subdomains.


![ezgif.com-video-to-gif.gif](https://blog-images-prod.s3.amazonaws.com/5ec867db-209c-4735-abf6-c0673921f5da)


If it's not working for you wait for some time to let DNS propagate changes. Try on a different device because Chrome caches the websites on disk.


### **Step 6: Handling www CNAME record :**


There are a couple of ways to handle this, 

1. We can add another CNAME record pointing to the same Cloudflare worker,
2. handle it in the worker code with JS or,
3. iii. Define a page rule such that anyone accessing [**www.kanvasai.com/***](http://www.kanvasai.com/*) gets automatically redirected to **https://kanvasai.com/***

![Untitled.png](https://blog-images-prod.s3.amazonaws.com/ee7d2053-2e64-47b6-8c2a-a08df2060b2b)


### **Step 7: Handling relative URL (hrefs) in your applications**


If your app uses an anchor tag and has subroutes, you should probably prefix those with subdomain names (/medium/about or /devto/about). You’ll understand why after trying it.


### **Step 8: SSL/TLS certificates between Cloudflare and your application server**


For Vercel and similar applications, SSL is generated by the platform itself. Hence, we need full security


For **Webflow**, we keep it flexible and let Cloudflare manage SSL certificates.


![Untitled.png](https://blog-images-prod.s3.amazonaws.com/af82d30c-bf05-4a7d-a3fa-432f91ab6232)


Cons of this approach

- Now, we’ll have to manage all our DNS records through Cloudflare.
- Adding a new record becomes a little tricky.

**In the follow-up article, we’ll see how to do this with SPAs.** Please comment if you want a tutorial on that topic.


> If you are trying to setup this reverse proxy and facing any issues, feel free to reach out or book a **free** consultation [here](https://calendly.com/niraj-mukta123/introductory-call?month=2023-09). I’ll be more than happy to help.


This article was published using[ Buzpen.](https://buzpen.com/) If you want to publish the same article to three major platforms at once, then do try out Buzpen.