Skip to main content

Custom Error Page

Cloudflare Custom Error Pages let you replace the default Cloudflare error messages with your own branded pages. Instead of showing Cloudflare’s standard error page when an issue occurs (like 500, 502, or 503 errors).

You can design and upload a custom HTML page that matches your website’s look and feel. This helps improve user experience, maintain brand consistency, and provide helpful information or navigation options during downtime or errors.

Task​

In this workshop, we’ll create a simple custom error page using Cloudflare Workers. By leveraging Workers, there’s no need to set up or maintain an additional server — the error page can be served directly from Cloudflare’s edge network. This approach not only ensures fast response times but also allows you to include troubleshooting details or helpful guidance within the error page itself.

  1. Create a Cloudflare Worker – Write a script to handle error responses and return a custom HTML page.

  2. Set Up Routing – Define routes so the Worker triggers when specific errors occur.

  3. Configure Error Handling – Map error codes (e.g., 500, 502, 503) to your Worker so it serves the custom error page.

  4. Test and Validate – Trigger errors intentionally to verify that the custom error page is displayed correctly.

Steps to create workers​

Step 1: Create a Cloudflare Worker​

  1. Go to the Cloudflare dashboard.
  2. Select your domain and navigate to the Workers tab.
  3. Click Create a Worker.
  4. Name your Worker (e.g., "Custom Error Page") and select Save.

Step 2: Write the Worker Code​

  1. In the Workers Editor, click the Cloudflare Playground button.
  2. Replace the existing code with the code below:
(() => {
var __defProp = Object.defineProperty;
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });

// src/index.js
addEventListener("fetch", (event) => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const headersObj = Object.fromEntries(request.headers);
const rayId = request.headers.get("cf-error-original-ray") || request.headers.get("cf-ray") || "Unavailable";
const userIp = request.headers.get("cf-connecting-ip") || "Unknown";
const currentTime = (/* @__PURE__ */ new Date()).toISOString();
const html = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>You've been blocked</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; margin:0; padding:0; background:#f8f9fa; color:#212529; display:flex; flex-direction:column; min-height:100vh;}
header { background:#dc3545; color:white; padding:1.5rem; text-align:center; }
main { flex:1; display:flex; align-items:center; justify-content:center; padding:2rem; }
.card { background:white; border-radius:8px; box-shadow:0 4px 10px rgba(0,0,0,0.1); padding:2rem; max-width:700px; width:100%; }
h1 { margin-top:0; font-size:2rem; color:#dc3545; }
p { margin:0.5rem 0; }
ul { text-align:left; list-style:none; padding:0; }
li { background:#f1f3f5; margin:0.25rem 0; padding:0.5rem; border-radius:4px; font-family:monospace; }
footer { text-align:center; font-size:0.9rem; color:#6c757d; padding:1rem; }
</style>
</head>
<body>
<header>
<h1>You've been blocked</h1>
<p></p>
</header>
<main>
<div class="card">
<h2>Technical Details</h2>
<ul>
<li><strong>Status code:</strong> 403</li>
<li><strong>Request Time:</strong> ${currentTime}</li>
<li><strong>User IP:</strong> ${userIp}</li>
<li><strong>RayID:</strong> ::RAY_ID::</li>
<li><strong>User Agent:</strong> ${request.headers.get("user-agent")}</li>
<li><strong>Workers Ray ID:</strong> ${rayId}</li>
</ul>
<p>${JSON.stringify(headersObj)}</p>
<p>Please share the above information with support for troubleshooting.</p>
</div>
</main>
<footer>Powered by Cloudflare Custom Error Pages</footer>
</body>
</html>
`;
return new Response(html, {
headers: {
"Content-Type": "text/html; charset=utf-8",
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0",
"Pragma": "no-cache",
"Expires": "0"
}
});
}
__name(handleRequest, "handleRequest");
})();
//# sourceMappingURL=index.js.map

Step 3: Deploy and Check​

Once done, you may click the Deploy button to have the Workers deploy to Cloudflare edge to production.

Once the deployment is done, click on the Preview button to view the error page you've been created.

Add routing to the workers​

Since we've done created a the custom pages, now we need to add the routing to the workers in order to have it to be displayed through the correct domain and path

Step 1: Accessing Workers​

Access to your Cloudflare dashboard, under the correct account, select Compute (Workers) > <WORKERS-NAME> > Settings

Step 2: Routing Config​

Under Domains & Routes, click on the Add button and select Route route-config

Select your zone and type the route as you wanted. For example:

  • Zone: your-zone.com
  • Route: <your-domain>/error-page

Then click Add Route button at the bottom right oh your page add-route

Step 3: Adding the to Custom Error Page​

Now, since we've created the workers, we can add the URL to the Custom Error Page so that when an error is returned, it will return this page instead of Cloudflare default error page.

Go to Account home and select the assigned zone. Then click on Error Pages and do the config here.

info

If you want it to apply to all the zone you have, you can select Manage Account -> Setting -> Error Pages to make the change.

For example, under WAF block click on the 3 dots at it's far right and select Edit waf-block-error-page

In the next page, select Custom Page and paste in the route you created just now in the Workers Route.

Now, when there's WAF block by Cloudflare, this custom error page will be returned instead of Cloudflare default block page.

Understanding Cloudflare Error Handling​

Cloudflare offers powerful mechanisms to control how errors are presented to your users. Here’s a breakdown of the components at play:

1. Error Pages (Presentation Layer)​

These are the pages your users actually see when an error occurs:

  • Default Error Pages

    • Automatically displayed for common HTTP errors (e.g., 500, 502, 503) or security blocks.
    • Basic, Cloudflare-branded, and functional—but not customizable.
  • Custom Error Pages (Zone- or Account-level on paid plans)

    • Allow you to replace default pages with your own HTML—styled, branded, and informative.
    • Useful for consistency in user experience—even during downtime or security events.
    • Zone-level settings override account-level settings.
    • Requires proper formatting and may use error tokens (like ::CLOUDFLARE_ERROR_500S_BOX::) for diagnostic info.

2. Custom Error Rules (Decision Logic)​

These rules determine when and which error page should be shown:

  • You can define logic using Cloudflare’s Rules engine to trigger specific error responses based on conditionals (e.g., HTTP response code, security triggers).
  • These rules take higher precedence over default or custom Error Pages.
  • You can serve:
    • Inline HTML, JSON, or text responses
    • Custom Error Assets (external pages fetched and stored on Cloudflare's edge network)
    • Optionally override status codes (e.g., convert a 500 into a 503)

How It All Works Together​

When an error occurs, Cloudflare follows this order:

  1. Look for a Custom Error Page at the account level
  2. Look for a Custom Error Page at the zone level
  3. Look for a matching Custom Error Rule at the account level
  4. Look for a matching Custom Error Rule at the zone level (highest priority)
  5. If a security product triggers a block (e.g., WAF), the rule-specific block response overrides all error pages or rules
  6. If none apply, serve the default Cloudflare error page

Key Takeaways​

  • Error Pages control what users see; Custom Error Rules control when and how errors are served.
  • Custom Error Rules have higher precedence and offer richer logic and flexibility.
  • You can serve inline or asset-based responses—including formatted HTML, JSON, and custom status codes.
  • Including diagnostic tokens (like ::RAY_ID::) enhances troubleshooting and support experiences.
  • Pay attention to size limits: error assets must be ≤ 1.5 MB and inline responses ≤ 10 KB.

For more info, check out Cloudflare’s documentation on: