200 status forward like Netlify Redirects with Cloudflare Transforms or Workers

I’m trying to make a blog hosted on blog.aitranscriber.tech appear on aitranscriber.tech/blog. I’ve gotten the GET requests to be routed properly and as you can see on the latter link, the page appears correctly.

I’m unable to route the POST requests though. The forwarding behavior I’m trying to achieve works well on Netlify which I’m using to point phreakyphoenix.tech/blog to blog.phreakyphoenix.tech. The relevant part of the netlify.toml file is below

  from = "/blog/*"
  to = "https://blog.phreakyphoenix.tech/:splat"
  status = 200

  from = "/_next/*"
  to = "https://blog.phreakyphoenix.tech/_next/:splat"
  status = 200

  from = "/api/*"
  to = "https://blog.phreakyphoenix.tech/api/:splat"
  status = 200

  from = "/js/*"
  to = "https://blog.phreakyphoenix.tech/js/:splat"
  status = 200

  from = "/newsletter/*"
  to = "https://blog.phreakyphoenix.tech/newsletter/:splat"
  status = 200

  from = "/ping/*"
  to = "https://blog.phreakyphoenix.tech/ping/:splat"
  status = 200

  from = "/_axiom/*"
  to = "https://blog.phreakyphoenix.tech/_axiom/:splat"
  status = 200

I ideally want to handle the post requests with transforms (basic forward with status 200), and I’ve a rule with the expression

(http.request.method eq "POST" and http.host eq "aitranscriber.tech")

set to dynamically rewrite the path to

concat("blog.aitranscriber.tech", http.request.uri.path)

On aitranscriber.tech/blog, when I make a POST request to the below URLs, I get an error 500

  1. https://aitranscriber.tech/ping/data-event
  2. https://aitranscriber.tech/_axiom/web-vitals
  3. https://aitranscriber.tech/ping/gamp
  4. https://aitranscriber.tech/api/collect
  5. https://aitranscriber.tech/ping/view
    and on the link https://aitranscriber.tech/api/newsletter/subscribe, I get the 500 error with the message Uncaught (in promise) SyntaxError: Unexpected token '<', "<!DOCTYPE "... is not valid JSON

I just want them to be forwarded to https://blog.aitranscriber.tech+path

These errors don’t appear on blog.aitranscriber.tech or on phreakyphoenix.tech/blog or blog.phreakyphoenix.tech, which is hosted with netlify and has the redirects mentioned above. You can also take a look at the errors by browsing the sites directly.

Do note aitranscriber.tech, the main site itself is on an Cloudflare Tunnel and since my server is off right now, you might see an error 1033, which is not related to this issue and this issue appears even when the Cloudflare Tunnel is active and the site is live.

In a gist:

  1. I want the POST requests to work with transforms just the way it works on Netlify, I’m no expert on routing and might be making a mistake in my Dynamic rewrite, please help me out if that’s the case.
  2. Else I want to implement the same with workers.

Some additional notes:

  1. My worker file, though not relevant to post requests is visible https://github.com/phreakyphoenix/misc/blob/master/worker.js, I did try to implement a MethodNotAllowed, and if I turn my transform off, then I go get the 405 error as defined in the status of the MethodNotAllowed. I couldn’t figure out how to meaningfully use the worker to do a simple 200 status forward.
  2. I also tried redirects, with status code 301 and 302, same expression and rewrite, which gives me a CORS error, complaining about cross-site requests not being allowed.

Solving this hurdle will help us host our blogs on subdirectories instead of subdomains with platforms like Hashnode, and I’d really appreciate the Cloudflare Community’s help in doing this.

PS: This is my first post, I couldn’t seem to post with any links so all links are backticked as pre-formatted text, thanks in advance for your help everyone.

Attaching my worker file directly as well

// keep track of all our blog endpoints here
const myBlog = {
  hostname: "blog.aitranscriber.tech",
  targetSubdirectory: "/blog",
  assetsPathnames: ["/_next/", "/js/", '/api/', '_axiom', '/ping/'] 

//set route *aitranscriber.tech/*
async function handleRequest(request) {
    const parsedUrl = new URL(request.url)
    const requestMatches = match => new RegExp(match).test(parsedUrl.pathname)
    if (request.method === 'POST') {
      return MethodNotAllowed(request);
      // console.log(`https://blog.aitranscriber.tech${parsedUrl.pathname}`);
      // return fetch(`https://blog.aitranscriber.tech${parsedUrl.pathname}`)
    // else method is GET

    // if its blog html, get it
    if (requestMatches(myBlog.targetSubdirectory)) {
      console.log("this is a request for a blog document", parsedUrl.pathname);

      const pruned = parsedUrl.pathname.split("/").filter(part => part);
      // // Drop the first element from the path if it matches the target subdirectory
      // // console.log(pruned[0])
      // targetPath = pruned.length > 1 ? `${pruned.slice(1).join("/")}` : "";
      // // console.log(targetPath);
      // // console.log(`https://${myBlog.hostname}/${targetPath}`)
      // var res = await(fetch(`https://${myBlog.hostname}/${targetPath}`));
      // res = htmlrewriter.transform(res);
      // console.log(pruned.length);
      // res = scriptadder.transform (res);
      // // console.log(res)
      // return res
      if (parsedUrl.pathname.startsWith('/blog/newsletter')){
        return scriptadder.transform (htmlrewriter.transform(await(fetch(`https://${myBlog.hostname}/${pruned.slice(1).join("/")}`))));
      if (pruned.length==1){
        return scriptadder.transform (htmlrewriter.transform(await(fetch(`https://${myBlog.hostname}`))));
        return htmlrewriter.transform(await(fetch(`https://${myBlog.hostname}/${pruned.slice(1).join("/")}`)));

    // if its blog assets, get them
    else if (myBlog.assetsPathnames.some(requestMatches)) {
      console.log("this is a request for other blog assets", parsedUrl.pathname)
      const assetUrl = request.url.replace(parsedUrl.hostname, myBlog.hostname);
      return fetch(assetUrl)

    console.log("this is a request to my root domain", parsedUrl.host, parsedUrl.pathname);
    // if its not a request blog related stuff, do nothing
    return fetch(request)
// }

class AttributeRewriter {
  constructor(attributeName) {
    this.attributeName = attributeName;
  element(element) {
    const attribute = element.getAttribute(this.attributeName);
    //add check for /blog start for nested scenarios
    if (attribute && !attribute.startsWith('https://')) 
      element.setAttribute(this.attributeName, '/blog'+attribute);

class ScriptAdder {
  element(element) {
      element.prepend('<script>function o(){location.href!=a&&(location.replace("/blog"+location.pathname),a=location.href)}var a=location.href;setInterval(o,100);</script>',{html: true});

const htmlrewriter = new HTMLRewriter()
  .on('a', new AttributeRewriter('href'))
  // .on('img', new AttributeRewriter('src'));

const scriptadder = new HTMLRewriter()
  .on('head', new ScriptAdder())

function MethodNotAllowed(request) {
  return new Response(`Method ${request.method} not allowed.`, {
    status: 405,
    headers: {
      Allow: 'GET',

addEventListener("fetch", event => {

In the additional notes pt 2, the CORS error I mentioned is this. This occurs when I replace the transform with the redirect, keeping the expression same.