Speed is a key feature of all the best developer tools, so the Console website needs to be fast and our CI/CD pipeline includes performance tests for every push.
Some pages are simple, but our developer tool reviews and list of all the current developer beta programs have a lot of entries. Despite this, both of these pages take no more than 1.5 seconds to load, and both score 100/100 on Google PageSpeed Insights.
This post is about how we automate our website performance testing.

Static sites are fast
We achieve most of our high performance by having a static HTML website. There is no backend, no database and no dynamic loading data from APIs. The site is built once and static HTML deployed.
We use Hugo as our static site framework. The site is split into the main theme, templates for specific pages, JSON for the Tools, Betas and Newsletter pages, and then markdown for the rest of the content. At build-time, we have a script which takes the content from our Google Sheets backend and reads it into the Hugo templates. This content doesn't change often, so it doesn't need a complex database backend.
This approach means the HTML itself is very small. For Tools it is only 56KB and Betas is 36KB. There are other assets like fonts, icons, images and CSS, but the browser can render the main content very quickly whilst the assets download in parallel. The largest components are the tool favicons we display for each vendor.

Distributed on Cloudflare
Cloudflare has one of the largest networks and we take advantage of that to put the site assets as close to our visitors as possible through the Cloudflare Workers Sites product. Once Hugo has built the HTML, we upload it to Cloudflare and it is deployed around the world.
Since the origin is within Cloudflare's network, it is able to serve the site from cache for most requests. We invalidate the cache only when a new deployment is uploaded, but even then it is simply loading the assets from Cloudflare's KV store. We do not run our own servers so there is no external origin.


Performance testing using k6
Every commit that is pushed to our GitHub repo triggers a GitHub Action to run performance tests. This builds the Hugo site and then deploys it to a special test domain that runs as a separate Cloudflare Worker.
We define several test plans which execute using the open source load testing tool, k6. The test plans are written in JS and issue a group of requests against specific pages to test whether they load within our performance SLAs, that there are no errors, and the page content is what we expect.
For example, the tools.js test will fail if p95 response time is greater than 1000ms and if more than 1% of requests trigger an error. The tests are run through the k6-action on GitHub Actions.
export const options = {
duration: "5s",
vus: 5,
thresholds: {
http_req_duration: ["p(95)<1000"],
errors: ["rate<0.01"], // <1% errors
},
};
Since the test site is not always in use, the results represent a worst case with no cached content from a cold start. Even so, our performance SLAs are strict. The homepage must load in less than 500ms, the tools page in 1000ms and betas in less than 1500ms. These are different because of the type of content and length for each page, but were chosen based on testing the page in multiple scenarios.

Securing tests using Cloudflare Access
The test site is secured by Cloudflare Access so that only authorized users are able to log in and view it. We also use this site as a staging environment to manually deploy experiments and draft pages that we want to test outside of our local development environment. Placing it behind an authenticated login gives us a safe area to play around and break things before it goes live. It also helps ensure we avoid Google thinking it is duplicate content.
Cloudflare Access supports service tokens so that automated systems can authenticate. We store the token as a secret on GitHub Actions and then the appropriate headers are sent by k6 when it issues the requests.
export default function () {
// Use service tokens to access the test URL behind Cloudflare
// https://developers.cloudflare.com/cloudflare-one/identity/service-auth/service-tokens
const res = http.get("https://test.console.dev/betas/", {
headers: {
"CF-Access-Client-Id": `${__ENV.CF_CLIENT_ID}`,
"CF-Access-Client-Secret": `${__ENV.CF_CLIENT_SECRET}`
}
});
No regressions
We're always working on improving the site and it deploys automatically from the main branch every day. Including performance testing as part of our build pipeline on every commit allows us to ensure that we don't accidentally introduce regressions. Too many websites take too long to load. Console is not one of them.