DEV Community

Cover image for Creating a Maintenance Page using a Cloudflare Worker
Mads Stoumann
Mads Stoumann

Posted on

Creating a Maintenance Page using a Cloudflare Worker

Last night, an Azure outage took down several sites my company maintains. While the servers were running, a critical downstream service — the headless CMS, Umbraco Cloud — was down. The result? Users saw a blank page. They did what any of us would do: they refreshed, and refreshed again. This flood of requests hammered the services, even though the root cause was elsewhere.

When a site's availability depends on other services, it is vulnerable to their downtime. A simple, robust maintenance page served from the edge is a cost-effective solution to this problem. It provides a clear, calm message to users, letting them know we're aware of the issue and working on it. This — hopefully! — stops the frantic refreshing that can overload the infrastructure.

This article will guide you through creating a maintenance worker using Cloudflare Workers. It's a "break glass in case of emergency" tool that can be activated in seconds when a site is in trouble, ensuring users are never left staring at a blank screen again.

We'll start with ...

The Basics

In your IDE of choice, create a simple HTML page:

<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
  <title>Site Maintenance</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <meta name="description" content="Site is under maintenance">
  <meta name="view-transition" content="same-origin">
  <style>
    body {
      background: Canvas;
      color: CanvasText;
      color-scheme: light dark;
      font-family: system-ui;
      line-height: 1.6;
      margin: 0 auto;
      max-inline-size: 1024px;
      padding: 0ch 4ch
    }
    h1 {
      font-size: clamp(2em, 5vw, 4em);
      line-height: 1.1;
    }
    p {
      font-size: clamp(1em, 2.5vw, 1.5em);
    }
  </style>
</head>
<body>
  <h1>We'll Be Right Back</h1>
  <p>The site is currently undergoing maintenance. Please check back later.</p> 
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

Now, in the Cloudflare Dashboard:

  1. Go to Compute & AI > Workers & Pages.
  2. Click on Create application, and just pick the "Hello world" template.
  3. Go to the new application, and replace the script with:
export default {
  async fetch(request, env, ctx) {
    // Get the client's IP address
    const clientIP = request.headers.get('CF-Connecting-IP');

    // Define the rate limit key based on the client's IP
    const rateLimitKey = `ratelimit:${clientIP}`;

    // Check rate limit in KV
    if (env.RATE_LIMIT) {
      // Fetch the current request count for the IP from KV
      const count = await env.RATE_LIMIT.get(rateLimitKey);
      const requestCount = parseInt(count || '0');

      // If rate limit exceeded (60 requests in a minute)
      if (requestCount >= 60) {
        return new Response('Too many requests. Please try again later.', {
          status: 429,
          headers: {
            'Content-Type': 'text/plain',
            'Retry-After': '60',
          },
        });
      }

      // Increment the request count and set expiration to 1 minute
      await env.RATE_LIMIT.put(rateLimitKey, (requestCount + 1).toString(), {
        expirationTtl: 60,
      });
    }

    // Maintenance page HTML
    const maintenanceHTML = `<!DOCTYPE html>
<html lang="en-US" dir="ltr">
<head>
  <title>Site Maintenance</title>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <meta name="description" content="Site is under maintenance">
  <meta name="view-transition" content="same-origin">
  <style>
    body {
      background: Canvas;
      color: CanvasText;
      color-scheme: light dark;
      font-family: system-ui;
      line-height: 1.6;
      margin: 0 auto;
      max-inline-size: 1024px;
      padding: 0ch 4ch
    }
    h1 {
      font-size: clamp(2em, 5vw, 4em);
      line-height: 1.1;
    }
    p {
      font-size: clamp(1em, 2.5vw, 1.5em);
    }
  </style>
</head>
<body>
  <h1>We'll Be Right Back</h1>
  <p>The site is currently undergoing maintenance. Please check back later.</p> 
</body>
</html>`;

    // Return the maintenance page immediately (no origin fetch)
    return new Response(maintenanceHTML, {
      status: 503,
      headers: {
        'Content-Type': 'text/html;charset=UTF-8',
        'Cache-Control': 'no-store, no-cache, must-revalidate',
        'Retry-After': '3600',
      },
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

Rate Limiting

We'll limit how many requests a single origin can make to our maintenance page.

  1. Go to Storage & Databases, then Workers KV
  2. Click on Create Instance, call the namespace "RATE_LIMIT"
  3. Next, go to Workers & Pages, then click on your maintenance worker.
  4. Click on Bindings, then Add binding
  5. Click on KV namespace, then on Add Binding
  6. Call your variable RATE_LIMIT

Testing

Now, the worker won't run unless we add a route. This is something we only want to do when the site is actually failing.

Go to your domain in Cloudflare, and select Workers Routes.

  1. Click Add route and fill out route — most likely your root domain followed by an asterisk, for example: browser.style/*
  2. Select your maintenance worker from the Worker dropdown
  3. Save, and after a minute or less, go to your domain.
  4. Do you see the maintenance page?

Maintenance page


Now, remove the route — and next time a service is down and your site fails, re-enable the route.


Why Not Just Update DNS?

You might wonder why you can't just point your DNS records to a different server with a maintenance page. The primary reason is time. DNS changes can take hours to propagate across the globe, as different DNS servers cache records for varying amounts of time. During a critical outage, you need a solution that works in seconds, not hours.

A Cloudflare Worker, on the other hand, is deployed to Cloudflare's entire global network almost instantly. When you enable the route, the change is reflected worldwide within moments, ensuring all users see the maintenance page immediately. This makes it a far superior solution for rapid response during an incident.


Other Services

While this article focuses on Cloudflare, other services offer similar edge computing capabilities that could be used to deploy a maintenance page. I haven't personally tested these, but they operate on similar principles:

  • AWS: Lambda@Edge and CloudFront Functions
  • Fastly: Compute@Edge
  • Vercel & Netlify: Edge Functions
  • Akamai: EdgeWorkers

Photo by Panumas Nikhomkhai, Pexels.

Top comments (0)