Making NordVPN’s homepage faster with Cloudflare Workers

Povilas Stračinskis

April 15, 2021

More than a year ago we deployed Cloudflare Workers and static HTML cache on NordVPN’s homepage. Today, we can confidently say that this has saved us from rewriting a massive portion of our code and helped to improve our site performance substantially. In this article, I’ll shed light on the problems we faced, and discuss how Cloudflare Workers helped to solve them.

close up of hands working on laptop with map of North America onscreen

A race against time

NordVPN’s homepage is a big and complex web app with many dynamic parts, including localization, device detection, partnership flows, pop-ups, and more. Most of these elements lived in the back-end code, meaning that we had to process all requests on our web servers. While this was acceptable at first, it became clear as time went on that this system limited the performance of the website.

As our business grew, so did the user traffic, which forced us to enlarge our operations in order to meet the growing number of requests. So we scaled out horizontally by adding more servers, but this soon became expensive and inefficient.

Moreover, we were vulnerable to user spikes. Even though the app cached most of its data in Redis for faster lookup, it still took some time to pull that data, construct an HTML page, and render it. This meant that we could only serve a very limited amount of requests per second.

A single promotion by a YouTube influencer or a massive sale on Black Friday was enough to put a lot of pressure on our servers. It was evident that we had to find another solution — and find it fast.

Searching for solutions

The first idea we considered was to rewrite our web app using a modern static site generator and move all the business logic to javascript. However, this would have been expensive and could take a long time to develop. Meanwhile, more and more users were visiting our site, so time was running short.

First, we tried to roll a custom static HTML cache in our NGINX web server. We wrote a Lua script that cached responses based on HTTP headers, URLs, and cookies. It helped a bit, but not nearly as much as we expected. Even with this cache, requests still had to be served by our servers, and we had a lot of dynamic pages that could not be cached.

diagram

Finally, one of our engineers suggested using Cloudflare's static HTML cache. That would allow us to cache our website content and serve it directly from the closest data center to the user without ever hitting our servers. We were already using Cloudflare CDN and WAF services, so it was a natural fit.

The problem was all those moving parts I talked about earlier. We could not cache our website yet — we had to make it static first.

Going static

Cloudflare Workers is a technology based on the Javascript V8 engine, which is used by the Chrome browser, Node.js, Electron, and other apps. These workers run javascript code on the Cloudflare network servers instead of the visitor's browser. Workers run with very low latency as the code is executed very close to the user's actual location.

data center

Cloudflare Workers has a number of uses, including:

  • Redirecting users.

  • Modifying response HTML and HTTP headers.

  • Implementing custom security rules.

  • Serving up JSON from the Cloudflare key-value store.

We realized that we could move some of the dynamic parts from our back-end code to a Worker and identified three main parts that were blocking us from using HTML caching:

  • Localization. Our site detects the users’ preferred language and remembers their choice.

  • Setting affiliate and coupon cookies. These cookies track user attribution across our subdomains. We needed to set them and redirect the user to the appropriate page.

  • Content personalization. Showing different content based on the user's country and URL parameters.

We started prototyping and figuring out how we could leverage this new technology in our favor. Cloudflare has a convenient playground environment which made Worker development relatively easy.

Here's an example Worker that handles one part of our requirements: it reads a coupon query parameter value and saves it to browser cookies:

1
addEventListener('fetch', (event) => {
2
event.respondWith(handleRequest(event.request));
3
});
4
async function handleRequest(request) {
5
let response = await fetch(request);
6
response = new Response(response.body, response);
7
const url = new URL(request.url);
8
const coupon = url.searchParams.get('coupon');
9
if (coupon !== null) {
10
response.headers.append('Set-Cookie', `coupon=${coupon}`);
11
}
12
return response;
13
}

Let's dig into this snippet:

  • We first add a fetch event listener and register a callback request handler function.

  • When a request comes in, we fetch a cached HTML version of the page and store it in the response object.

  • We then check if a coupon query parameter is set and if it is, we construct a cookie and set the `Set-Cookie` header on the response object.

  • In the end, we return the response object, completing the callback.

diagram

Since we’re using the static HTML cache, the entire request is handled by Cloudflare. Our web servers can take a break.

Results

After we released the Worker and turned on the static HTML cache, we saw a sharp decrease in server requests. Our server costs went down significantly.

Let’s take a look at our private IP check-up page, with the URL https://nordvpn.com/what-is-my-ip/. On a random day, this page got around 77,000 requests, almost 65,000 of which were cache hits. That means almost 85% of traffic didn't have to reach our servers.

graph of website traffic

We now have a much more scalable and resilient system, because any spikes in user traffic are buffered by the Cloudflare network. This alleviates the pain of having to constantly scale our servers up and down as the load changes.

Conclusion

When we released the Worker to production, we were very impressed with the speed at which it processed requests. Response times were decreased substantially, which improved our site performance score. Thanks to Workers, the app is now faster and more resilient to traffic spikes and attacks. We were able to avoid a massive app rewrite and also decreased our server costs.

Cloudflare Workers is a technology that will make a big impact on the web development industry. It provides new ways to solve old problems of latency and scalability. The solution explained in this article is just a small part of what this technology can do, and we're experimenting and expanding the ways we use Workers as our understanding grows.