How to use Webflow to host parts of your site (Cloudflare Workers)

A workaround to Webflow’s hosting limitations + CMS sub-category routing with wildcard paths.

In all likelihood, you’re reading this article because you’ve used or played around with Webflow in the past. If not, I highly recommend you check them out. Webflow is a web design platform that’s revolutionizing how we build for the web by making it dead simple to deploy custom designed sites without writing code.

One of the best use-cases for Webflow is to build your marketing site, blog, etc… in isolation from your core app (especially great for handing off this responsibility to a marketing team). In Webflow, however, you need to point your domain to Webflow’s servers in order to deploy and take advantage of all their features. Unfortunately, in many cases, this may not be possible (ex: using your root domain for your app). One solution is to use a subdomain to host your app (i.e. app.example.com) and point your root domain to Webflow. This is obviously not ideal in many cases.

If you’re just looking to use Webflow for your home page, marketing site, blog, or other static pages, you’re really only left with the option of exporting the code. But exporting the code means you can’t take advantage of some awesome features like the CMS or forms.

In looking to try and solve this problem, every solution I’ve come across involved using a reverse proxy. This approach, while it may work, is complicated to setup and often not possible depending on your tech stack.

The solution I came up with is to use Cloudflare Workers to dynamically route requests to the correct host. Note: This only works if your DNS provider is Cloudflare.

Getting Started

Cloudflare Workers are an extremely powerful tool that allow you to run JavaScript code before any request to your application is returned. Workers will run you $5/month, but are totally worth it.

To get started, visit your Cloudflare dashboard and click the Workers tab under the appropriate domain. Click the Enable Workers button to start using Workers and launch the editor. Once there, you’ll be prompted to create a new script. Give your script a name and jump into the editor. The first thing you’ll see is the following code:

addEventListener(‘fetch’, event => {

event.respondWith(handleRequest(event.request))

}) /**

* Fetch and log a request

* @param {Request} request

*/

async function handleRequest(request) {

console.log(‘Got request’, request)

const response = await fetch(request)

console.log(‘Got response’, response)

return response

}

The handleRequest function is where the magic happens. Each request enters this function and returns a response to send back to the requesting party. We’re going to be modifying this function and injecting a bit of logic to tell Cloudflare where to route our reqeusts.

Replace the existing handleRequest function with the following code:

async function handleRequest(request) { //1. Parse the URL to get some useful info

let parsedUrl = new URL(request.url)

let pathStripped = parsedUrl.pathname.replace(/^(.+?)\/*?$/, “$1”) //2. Webflow URL to forward your pages to

var forwardHost = “https://proxy.example.com" //3. Paths you’d like to match and forward

var urlPatterns = [“/”, “/about”] //4. Match the routes, and forward the request

if (urlPatterns.indexOf(pathStripped) > -1) {

return await fetch(forwardHost + parsedUrl.pathname, request)

} //5. Return deafult response

const response = await fetch(request)

return response }

In Step 1 we are just parsing the incoming URL to retrieve its components. This will allow us to extract the path of the url. We need to strip out any trailing slashes to ensure our matching patterns work correctly.

In Step 2 we define the URL we’d like to proxy this request to. Replace forwardHost with the URL your Webflow site is deployed to. You can find it by going to the top right-hand corner in your project and clicking on Publish. Note: while it’s currently possible to use the Webflow staging domain to proxy your site, this isn’t technically supported by Webflow and is not allowed. You must use a custom domain to proxy your site. I highly suggest creating a subdomain (ex: proxy.example.com) and pointing it to Webflow. Read more on how to setup a custom domain.

Step 3 is where we define an array of the paths we’d like to match and send Webflow’s way. Here, just create an array of path names. Important: put the patterns in the urlPatterns array in order of importance. The code will stop at the first pattern it finds.

In Step 4 we just check if the incoming path matches any of the paths in our array from Step 3 . If we find a match, we return a fetch request to our Webflow URL. If no match is found, the function will fall through to the default response.

Tip: You can preview your Worker on the right hand side of the page before you deploy it to ensure everything is working right.

Click “Save and Preview” and navigate to one of your routes — you should see your Webflow site. And that’s it! Don’t forget to deploy your script once you’ve validated it works as intended.

If that’s all you need, you can stop reading here. But, for more advanced functionality, read on.

Wildcard Routing

If you’re using Webflow’s CMS, you’re likely wondering how we can route those pages without defining every URL (wildcards). For example, if we wanted to route /blog/{article-slug} to Webflow, we wouldn’t want to add every blog url to our urlPatterns array , we’d want a way to match and route /blog/* , where * is a wild card.

Luckily, with a few modifications to our function above, we can make this work. First add the following function to your script to help convert url patterns to regex rules:

function convertToRegexString(pattern) {



//Escape Regex expression

pattern = pattern.replace(/[|\\{}()[\]^$+*?.-]/g, '\\$&') //Build Regex string

return "^" + pattern.replace(/\\\*/g, '.*').replace(/\\\?/g, '.') + "$" }

Then, modify Step 4 in the handleRequest function and replace it with:

//4. Match the routes, and forward the request

if (urlPatterns.some(pattern => (new RegExp(convertToRegexString(pattern))).test(pathStripped))) {

return await fetch(forwardHost + parsedUrl.pathname, request)

}

Now, you can add wild card routes into your array as follow:

//3. Paths you’d like to match and forward

var urlPatterns = ["/blog/*"]

Huzzah! You’re now forwarding wild card url patterns to Webflow.

Bonus Tip 1: Routing to different path

In some case you might need to route a URL to a different Webflow path. For example, if you wanted to send /about to a page in Webflow that used the path /about-us .

To do this, we’re going to allow ourselves to define a path along with it’s destination in the urlPatterns array. Check out the example below — the first index in the array is the pattern to match and the second is the destination.

//3. Paths you’d like to match and forward

var urlPatterns = ["/home", ["/about", "/about-us"]]

Modify Step 4 in the handleRequest function:

//4. Match the routes, and forward the request

for (var i = 0; i < urlPatterns.length; i++) { //4.1 Get the pattern, check for array

var pattern = urlPatterns[i]

var forwardPath = parsedUrl.pathname

if (Array.isArray(pattern)) {

forwardPath = pattern[1]

pattern = pattern[0]

}



//4.2 Check for a match

if ((new RegExp(convertToRegexString(pattern))).test(pathStripped)) {

return await fetch(forwardHost + forwardPath, request)

}

}

Bonus Tip 2: CMS sub-category routing

One big limitation of Webflow’s CMS you’ll likely run into is that you can’t use nested routes to represent your nested CMS objects. For example: you have a blog with different categories, where each blog post can be assigned a category. In Webflow, if you wanted to create category pages, you’d need to do something like this /blog-categories/{category_name} . But this isn’t very pretty. A better way of doing this would be to use /blog/category/{category_name} .

To do this, we just need to modify line 4.2 from Bonus Tip 1, and change it to:

//4.2 Check for a match

if ((new RegExp(convertToRegexString(pattern))).test(pathStripped)) {

var parts = pathStripped.split('/');

var ending = forwardPath.substr(-1) == '/' ? parts[parts.length - 1] : ""

return await fetch(forwardHost + forwardPath + ending, request)

}

Now, in your urlPatterns array, define a sub-category route by appending a trailing slash to the destination. This will tell our function that we should append the ending of our route to the new destination.

//3. Paths you’d like to match and forward

var urlPatterns = [["/blog/category/*", "/blog-categories/"]]

Important: Only add the trailing slash to the pattern if you’d like to append the remaining path to the new route.