Automated Failover with Cloudflare Workers

Published on February 7th 2026

I manage a high-availability API service, `api.example.com`, that requires maximum uptime. This makes server maintenance, upgrades, and migrations challenging, as I cannot simply inform users of a multi-hour outage.

Initially, I set up a redundant API service on a second server with its own subdomain, `api2.example.com`. With proper NGINX configuration, this secondary server could accept requests for both domains. To perform maintenance, I would manually update the Cloudflare DNS settings for `api.example.com` to point to the secondary server's IP address. While this worked, it was a manual process, and DNS propagation delays meant the switch wasn't instantaneous.

I wanted to automate this failover process using Cloudflare Workers. Since Workers execute at the edge before DNS resolution, they can intercept requests, check the primary server's response, and route traffic accordingly. My goal was to check if the main server is healthy, and if not, automatically fallback to the secondary server.

It is important to note that this is not an HTTP redirect. The Cloudflare Worker intercepts the request and proxies it to the appropriate upstream server transparently.

Instructions

  1. DNS: Create proxied A records for api.example.com and api2.example.com pointing to their respective origins.
  2. Worker: Create a new worker and deploy the script below.
  3. Route: Add a route for api.example.com/* pointing to the worker (Failure mode: Fail open).
export default {
    async fetch(request) {
        const primaryUrl = 'https://api.example.com';
        const fallbackUrl = 'https://api2.example.com';

        const url = new URL(request.url);
        const path = url.pathname + url.search;

        // Try primary
        try {
            const response = await fetch(primaryUrl + path, request);

            // If origin responds with a redirect (301/302), return it
            if ([301, 302].includes(response.status)) {
                return new Response(null, {
                    status: response.status,
                    headers: { 'Location': response.headers.get('Location') }
                });
            }

            // Otherwise, return normal response
            if (response.ok) {
                return response;
            }
        } catch (e) {
            console.error('Primary failed:', e);
        }

        // Fallback: try the same logic on secondary
        try {
            const response = await fetch(fallbackUrl + path, request);
            if ([301, 302].includes(response.status)) {
                return new Response(null, {
                    status: response.status,
                    headers: { 'Location': response.headers.get('Location') }
                });
            }
            return response;
        } catch (e) {
            console.error('Fallback failed:', e);
        }

        return new Response('Service unavailable', { status: 503 });
    }
};

Conclusion

That's it! Once the DNS entries are configured and the Worker is deployed, the failover logic operates automatically at the edge. This setup ensures that if your primary server goes down or needs maintenance, traffic is instantly routed to the backup server without any manual intervention or DNS propagation delays.