I just discovered Cloudflare Pages and was looking to move over my sites from Netlify to Cloudlfare Pages, and a blocker is that I use a feature of Netlify that is not supported by Cloudflare Pages. Netlify has “200 OK” redirects, as documented e.g. here:
Redirects with this status code will change the server response without changing the URL in the browser address bar. This is used for rewrites and proxying.
and crucially, this can be across Netlify sites, as mentioned here:
With internal rewrites, you can proxy from one Netlify site to another.
/netlify-site/* https://my-other-site.netlify.app/:splat 200
(with obvious restrictions like both sites having to be owned by the same “team”/user).
I use this feature heavily, e.g. on my personal website, I have a bunch of /foo
and /bar
URL prefixes that are each being served from different Netlify sites.
But Cloudflare pages does not seem to support this feature, as documented here:
Proxying will only support relative URLs on your site. You cannot proxy external domains.
Some prior community discussions on this topic seem to be (sorry as a new user I could only link to one of them):
It would be nice if this feature could be added.
1 Like
Hi there,
Rewrite links using workers might be what you’re looking for:
Take care.
Hi @shreevatsa
Maybe something like this
```js
export default {
async fetch(request) {
const destinationURL = "https://example.com/new-page";
return fetch(destinationURL);
},
};
This code snippet will redirect requests to https://example.com/new-page
while keeping the status as 200.
workers/examples/redirect
Thanks @mcorreia and @louise2. By following the documentation for Pages Functions, I was able to do the following which seems to work (minimal version, for just one site proxied through):
export function onRequest(context) {
const prefix = '/foo/';
let path = context.functionPath;
if (path.startsWith(prefix)) {
return fetch('https://my-other-site.pages.dev/' + path.substring(prefix.length));
}
}
and adding a _routes.json
in the output directory:
{
"version": 1,
"include": [
"/foo/*"
],
"exclude": []
}
One downside here is requests are no longer “free”: Pricing · Cloudflare Pages docs says
On both free and paid plans, requests to static assets are free and unlimited. A request is considered static when it does not invoke Functions.
and
Requests to your Pages Functions count towards your quota for the Workers Free plan
Similarly:
All Pages Functions are billed as Workers
— and note that all these Pages Functions run on the “root” site, so everything counts against the same site’s limits.
Practically it’s not a problem for me personally as I don’t expect to hit the 100,000 requests/day limit for free workers, but it is still a difference from Netlify / GitHub Pages / etc which provide hosting at /foo from a different site, without having to set up workers/functions.
The feature request at Deploying pages sites to subdirectories (currently 20 votes; was 16 just a few days ago when I saw it) seems related to this. In the sense that if I could deploy my-other-site
site to /foo/
(in the sense of that feature request) that would be equivalent to what I’m seeking here.
Apart from the links above, here are the links I could not post in the original post:
And some more recent requests:
And for what it’s worth, as an example, to redirect from my shreevatsa.net/ryder
to another CF Pages website, the code I’m currently using is something like this:
- create a
functions/ryder/
directory containing a file [[catchall]].js
.
- Have its contents be something like:
export function onRequest(context) {
try {
let path = context.functionPath;
/*
Note about missing trailing slash.
The ryder/ website redirects to index/index.html.
- Visiting /ryder/ properly redirects to /ryder/index/index.html
- Visiting /ryder would redirect to /index/index.html -- BAD!
So a visit to /ryder should first result in a redirect to /ryder/
Unfortunately, "return Response.redirect('/ryder/', 302)" fails with "TypeError: Unable to parse URL: /ryder/"
*/
if (path == '/ryder') {
// https://github.com/denoland/deno/issues/20674#issuecomment-1742782646
return new Response(null, {
status: 302,
headers: {
Location: "/ryder/",
}
});
}
if (path.startsWith('/ryder/')) {
return fetch('https://ryder-155.pages.dev/' + path.substring('/ryder/'.length));
}
return new Response(JSON.stringify(context.functionPath));
}
catch (error) {
// https://stackoverflow.com/questions/18391212/is-it-not-possible-to-stringify-an-error-using-json-stringify
return new Response(JSON.stringify(error, Object.getOwnPropertyNames(error), 2));
}
}
This basically intercepts any request to /ryder/foo
and replaces it with a response formed by fetch
to the URL on the other website. This counts (is billed) as a function/worker invocation.