Can Pages do rewrites (not redirects)

Hi. I'm trying Cloudflare Pages as an alternative to Firebase hosting for my existing site. I'm having a pretty positive experience so far, especially with the build-from-git integration. But I've come to an issue which seems to block the migration. Amongst some of the routes my static site uses I have ones that have a pattern such as /ws/<wsid>/viewtypexxx, which I would like to be rewritten as /ws/viewtypexxx.html. By "rewritten" I mean the httpd server serves the content of the latter when the client requests the former. The JS in my page parses the wsid value out of the location for itself so the right content is loaded dynamically after initial page load. I'm not finding any solution in docs, but it doesn't say clearly that it isn't supported either, and rewrite rules seem common enough these days in other hosting I've been uisng over the last couple of years. Are they supported?
6 Replies
texan
texan6d ago
Not out of the box, but you could create your own _middleware.js which does the rewrite for you
export const onRequest: MiddlewareHandler = async ({ request, next }) => {
const url = new URL(request.url)
const match = url.pathname.match(/^\/ws\/[^\/]+\/(viewtype[^\/]+)$/)

if (match) {
// Rewrite to /ws/viewtypexxx.html
url.pathname = `/ws/${match[1]}.html`
return fetch(url.toString(), request)
}

return next()
}
export const onRequest: MiddlewareHandler = async ({ request, next }) => {
const url = new URL(request.url)
const match = url.pathname.match(/^\/ws\/[^\/]+\/(viewtype[^\/]+)$/)

if (match) {
// Rewrite to /ws/viewtypexxx.html
url.pathname = `/ws/${match[1]}.html`
return fetch(url.toString(), request)
}

return next()
}
(https://developers.cloudflare.com/pages/functions/middleware/)
Akira Kurogane
Akira KuroganeOP5d ago
Thank you for showing that example, and clearing up there isn't a rewrite config option in Pages. Part of my reason for trying out Cloudflare was to see if workers were going to be better for me than firebase's "cloud functions", for something else unrelated to this question. So I'm not against trying a worker. And you're making it very easy for me by providing the sample, much appreciated. But doesn't this introduce cost, and latency. Sounds like two servers are going to be involved - a workers server (with it's v8 js runtime daemon?) and then a network hop to the static content-serving httpd.
texan
texan5d ago
Yeah, technically there is a small overhead, but the worker is executing at the edge close to your users. I think even with this type of setup you're probably better than firebase (which could definitely have higher cold start times unless you pay for reserved instances)
Akira Kurogane
Akira KuroganeOP5d ago
At this point if hadn't been planning to use workers already, for a different reason, I think I would move on. I appreciate that the javascript workers support high flexibility, but on the other hand this one rule becomes its own script, kept in subdirectory in the source that I would bet other collaborators won't think to look into for site config, when they're checking it. I know what I'll do, I'll probably add a dummy _redirects file, because that sounds like it could be related, _rewrites file (which means nothing to Pages hosting, but it'll be noticed) and write a comment in there explaining the middleware worker is doing it. But I will try it. This was not successful. Firstly adding the class specifier(?) MiddlewareHandler was reported as a syntax error. And it doesn't appear in the documentation either. So I removed it, after which the build with my new functions/_middleware.js file succeeded. But using this middleware function the page request to https://xxx.com/ws/<wsid>/ts was redirected by a HTTP 308 response to https://xxx.com/ws/ts, not 'rewritten' by supplying the content of /ws/ts[.html] at the request path of /ws/<wsid>/ts. ("ts" is one value for what I described as viewpagexxx yesterday.) When I saw the fetch() call in the example kindly provided yesterday I had faith it would work as I was thinking, but somehow it's become this 308 redirect instead.
Akira Kurogane
Akira KuroganeOP5d ago
No description
Akira Kurogane
Akira KuroganeOP3d ago
I worked out a correct function. The key thing was using env.ASSETS.fetch(), not plain fetch() (or whatever it is that fetch() is in cloudflare worker).
export const onRequestGet = async ({ request, env, next }) => {
const url = new URL(request.url)
const match = url.pathname.match(/^\/ws\/[^\/]+\/([^\/]+)$/)

if (match) {
url.pathname = `/ws/${match[1]}.html`;
return env.ASSETS.fetch(url);
}

return next()
}
export const onRequestGet = async ({ request, env, next }) => {
const url = new URL(request.url)
const match = url.pathname.match(/^\/ws\/[^\/]+\/([^\/]+)$/)

if (match) {
url.pathname = `/ws/${match[1]}.html`;
return env.ASSETS.fetch(url);
}

return next()
}
I'm using onRequestGet rather than onRequest simply because I know that is the only HTTP verb that will be used on these routes. If it does, then that documentation needs improvement. Currently it says: "Redirects" / "Yes" (supported), but only with HTTP codes "(301, 302, 303, 307, 308)" "Rewrites" / "No" (not supported). Is the thing I learnt to call "rewrite" rules from other hosting solutions called proxying here? FYI some other hosting (and dev server) where the term is called rewrite rule: https://firebase.google.com/docs/hosting/full-config#rewrites https://github.com/lwsjs/local-web-server/wiki/How-to-rewrite-URLs-to-local-or-remote-destinations https://docs.digitalocean.com/products/app-platform/how-to/url-rewrites/ I think it might have become popular when SPAs became a common thing, not that I'm using an SPA.

Did you find this page helpful?