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>
Now, in the Cloudflare Dashboard:
- Go to Compute & AI>Workers & Pages.
- Click on Create application, and just pick the “Hello world” template.
- 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',
      },
    });
  }
};
  Rate Limiting
We’ll limit how many requests a single origin can make to our maintenance page.
- Go to Storage & Databases, thenWorkers KV
- Click on Create Instance, call the namespace “RATE_LIMIT”
- Next, go to Workers & Pages, then click on your maintenance worker.
- Click on Bindings, thenAdd binding
- Click on KV namespace, then onAdd Binding
- 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.
- Click Add routeand fill outroute— most likely your root domain followed by an asterisk, for example:browser.style/*
- Select your maintenance worker from the Workerdropdown
- Save, and after a minute or less, go to your domain.
- Do you see the 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

 
		
 
			