Search code examples
node.jstypescriptcloudflare-workers

Proxy server cannot handle relative redirects


I am writing an HTTP proxy and as I need to use Cloudflare Workers, my choice of libraries was very limited, so I decided to create it myself.

Here is the code, which works pretty well:

export default {
    async fetch(request, env, ctx): Promise<Response> {
        const { search, pathname } = new URL(request.url);
        const url = pathname.slice(1);
        if (!url) {
            return new Response("Please specify a target URL.", { status: 400 });
        }

        let targetURL;
        try {
            targetURL = new URL(url);
        } catch {
            return new Response("Invalid URL.", { status: 400 });
        }

        const forwardedRequest = new Request(targetURL, request);

        const res = await fetch(forwardedRequest);
        const response = new Response(res.body, res);

        return response;
    },
} satisfies ExportedHandler<Env>;

The system is simple: the user makes a request to https://<domain>.workers.dev/<url> and the request is sent to url with the headers, method and body received by the proxy server.

Unfortunately, it does not handle relative redirects. If a relative redirect is encountered, the new path is simply appended to the proxy server's domain, resulting in an invalid URL. For example, when I forward a request to https://httpbin.dev/relative-redirect/2, instead of the proxy server redirecting to https://<domain>.workers.dev/https://httpbin.dev/relative-redirect/1, it goes to https://<domain>.workers.dev/relative-redirect/1, which breaks the redirect.

However, it works as expected with https://httpbin.dev/absolute-redirect/2.

Do you have any idea on how to fix this? I do not run into the same issue when using axios.


Solution

  • The redirect location is in the response's Location header. You will have to rewrite it. You can do so by changing this part of your code:

    const response = new Response(res.body, res);
    
    return response;
    

    To something like:

    const response = new Response(res.body, res);
    
    let location = response.headers.get("Location");
    if (location) {
      response.headers.set("Location", rewriteLocation(location));
    }
    
    return response;
    

    (You'll need to figure out what logit to put in rewriteLocation().)