How to replace characters in incoming URLs?

Hello,

I am trying to replace certain characters in requested URLs and then send a redirect to the modified URL. I have tried to use Netlify functions, but it seems the deployment of these only works when a git repo is connected. We currently use only manual deployments.
I have also tried to use a _redirects file. This works, but doesn’t cover all my use cases (only some using “:splat” where the path does not change); specifically it doesn’t seem to have a way to dynamically modify the requested URL.
So, how can I replace characters in URLs in a manual deployment setup? Thanks!

Kind regards,
Martin

@martinwunderlich-cel What do you mean by “manual deployments”?

If you mean Drag and Drop that doesn’t support functions.

Deploying via the CLI does as indicated in the documentation:
https://docs.netlify.com/cli/get-started/#deploy-directories

There is an old conversation regarding it all here:

Thank you, @nathanmartin. I have managed to deploy the function now using the CLI.

However, I am still stuck on the actual task. The char replacement function is being triggered by adding this to the _redirects:
/* /.netlify/functions/replacechars 200

I am wondering, if this wouldn’t perhaps lead to an infinte loop of redirects, because the script is called not only once.

Secondly, I am not able to extract the host from the request headers directly, which is odd.

This works:

let host = "";
  console.log('#### headers: ', req.headers);
  req.headers.forEach((value, name) => {
    if (name == 'host') {
      console.log("#### host found!", value);
      host = value;
    }
  });

This does not work:

console.log('#### host from headers: ', req.headers['host']);

This doesn’t work either:

console.log('#### host from headers: ', req.headers.get('host').value);

The host is undefined in both these cases.

Any ideas why?
Thanks!

Regards,
Martin

A related question: Constructing the redirect response using the Netlify Response object does not work either. This is the code snippet:


    // Redirect to the modified URL
    return new Response('', {
      status: 301,
      headers: {
        "Location": modifiedUrl
      }
    });

This leads to the following error:

This function has crashed
An unhandled error in the function code triggered the following message:

TypeError - import_functions.Response is not a constructor

Is this a special Response object and if yes where can I find documentation? Or is it this standard one?

Cheers,
Martin

OK, so answering my own question: To extract the host you first need to convert the header list to a an object like this:

const headersObject = Object.fromEntries(req.headers);
console.log('#### host from headers: ', headersObject['host']);

Apparently, this line was incorrect and importing the wrong (or incorrect) type:
import { Response } from "@netlify/functions";

After deleting this, the redirects work.

Final issue: Using Netlify _redirects, how I can prevent requests being touched more than once by the function?
This is how requests are being routed through the function:

/*			/.netlify/functions/replacechars 200

I know I could probably add some request param to modified URLs, but maybe there is a better way.

@martinwunderlich-cel What you’ve suggested is one way to handle it.

The crux of it being that you can’t use a single catch all rule of /* to push everything to a function, then expect that the 301’s returned to the user not also be caught by that same catch all when the users browser then requests them.

If you’re happy to have the function execute on every request, then you could have it return either 301 or 200 (and the content), depending on what it has performed on the URL.

Thanks a lot, @nathanmartin. I figured out another way. Fortunately, the URLs to be redirected have a common root and this common root is not present in the redirect targets. So, instead of redirecting /*to the replace function, I can apply redirects to /commonRoot/*. Then, in the replace function I also change the commonRoot to a modifiedRoot and then do the remaining redirects based on this modifiedRoot. Thus, I am able to prevent an infinite redirect loop. This only works in my special case, but that’s good enough for now. :smile: (“The perfect is the enemy of the good”, said Voltaire)

Excellent, I was going to mention that usually when people are redirecting there is some common initial path fragment they can use to more easily identify origin/destination, but your use of /* seemed fairly intentional.

Glad you managed to get it sorted out!