Can a Worker return 503 if the origin is not reachable? (fetch error handling)

I really enjoy using Cloudflare Workers. However, I can’t figure out how to handle certain origin fetch errors. In below example a nonexistent remote host is used. Since connection attempts will fail, I would expect to get a 503 response but whatever I try, the response is always a Cloudflare error page (1016 and 502 in below case) and not my desired custom error handling response.

async function handleRequest(request) {

    let subrequest = new Request("http://djskdjhksgdksjhdkjh.com", request)
   
    try {
        return await fetch(subrequest)
    } catch (exception) {
        return new Response("Service temporarily unavailable", {status: 503})
    }

}

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

Actual use case: I am trying to implement a Worker that returns 503 if the origin can’t be reached / is down. How do I do that?

Fetch will only throw if there is a network error. It will not throw for a 400, 500, a timeout, etc.

A fetch() promise will reject with a TypeError when a network error is encountered or CORS is misconfigured on the server-side, although this usually means permission issues or similar — a 404 does not constitute a network error, for example.

MDN

You can use response.ok or response.status to determine what to do once you receive the response.

You can even implement a timeout yourself:

Thank you, @pier!

Unfortunately, to me it seems as though Cloudflare adds some magic in the background because I was actually able to produce a custom 503 response using this. However, it only worked in the beginning. It quickly changes back to the 502 Cloudflare default error page. My guess is that the Cloudflare environment remembers unreachable remote servers and subsequently rejects any new connection/fetch attempt for a certain period of time. This would totally make sense but it’s only an assumption.

Can somebody from Cloudflare please shed some light on this?

If my assumption is correct, is there a way to handle Cloudflare errors like 5xx and 10xx with Workers? Since custom error pages require Cloudflare Pro, could upgrading the account maybe make it possible to handle these errors with Cloudflare Workers?

Your workers can throw a 500 error. I know I’ve seen plenty of those with mine :slight_smile:

Maybe you are right and CF handles 503 and 502 differently. I’ve never tried it but you could maybe return a 500 with some html.

@pier, yes, Cloudflare Workers definitely can return 500 errors, even 503 and other HTTP statuses.

My problem is that the environment seems to automatically return a default Cloudflare error in certain fetch scenarios, e.g. Bad Gateway (502), Web server is down (521) and Origin DNS error (1016).

Mentioning @KentonVarda to get a confirmation so that I can stop trying to get this to work. :wink:

1 Like

Hi @user6251,

Your worker as written will only return your custom 503 error if await fetch() throws an exception. However, an unreachable backend will not necessarily cause await fetch() to thrown an exception. Instead, fetch() itself will likely return a Response with status = 502 and statusText = "Bad Gateway, or some similar error. In this case, your worker as written will return that response directly to the client, so that’s what the client ends up seeing.

If you would like to handle these errors, you will need to do something like this:

async function handleRequest(request) {
    let subrequest = new Request("http://djskdjhksgdksjhdkjh.com", request)

    let response = await fetch(subrequest);
    if (response.status == 502) {
        return new Response("Service temporarily unavailable", {status: 503})
    }

    return response;
}

addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request))
})

Note that the reason this happens, vs. fetch() throwing an exception, is because the Workers Runtime does not directly connect to the target server. Instead, it connects through an HTTP proxy (the same HTTP proxy that non-Workers Cloudflare requests go through). When an HTTP proxy cannot connect to the requested server, it is expected to return a 502 Bad Gateway or similar error. The HTTP proxy protocol doesn’t define any way to tell fetch() to throw an exception – it doesn’t know anything about fetch(). You might have observed that in your browser, fetch() to an unknown host does throw an exception – but this is only true if your browser is configured to talk to the internet directly (as most are). If you configure your browser to go through an HTTP proxy, you will see similar behavior to what your worker sees.

2 Likes

Thank you so much for your quick and detailed response, @KentonVarda!

My apologies for thinking so complicated. With your helpful instructions I was able to implement the desired solution. The worker is now responding with a 503 whenever the actual response.status >= 500.

Based on this article, I was also able to implement streaming of the original response body of the error page.

That means in case of a problem, the server will always return a generic 503 error, accompanied by a specific and therefore helpful Cloudflare error page. :slight_smile:

Again thank you very much for your help and have a wonderful weekend!