I recently (finally) finished moving all articles from my old wiki — to this blog. Once that was done I needed to forward all URLs to their new location.

My site is hosted with AWS CloudFront, so I made a simple redirect function and deployed it to AWS Lambda@Edge.

Table of contents

New function

In the AWS console; navigate to the Lambda service. Make sure the location is N. Virginia, this is required for Lambda@Edge.

Make a new function:

  • Click Create function, top right
  • Author from scratch
  • Enter a function name
  • Choose Node.js 12.x (latest one supported by Lambda@Edge)
  • Click Create function

The function consists of three files:

  • index.js, the actual function script
  • redir.json, list of redirects
  • regex.json, list of regex redirects

My index.js looks like this;

const redirectsRegex = require('./regex.json').map(
  ({ source, destination }) => ({
    source: new RegExp(source, 'i'),
    destination
  })
);

const redirectsStatic = require('./redir.json').map(
  ({ source, destination }) => ({
    source,
    destination
  })
);

exports.handler = async (event) => {
  const request = event.Records[0].cf.request;
 
  for (const { source, destination } of redirectsRegex) {
    if (source.test(request.uri)) {
      return {
        status: '301',
        statusDescription: 'Moved Permanently',
        headers: {
          location: [{ value: destination }]
        }
      };
    }
  }
  
  for (const { source, destination } of redirectsStatic) {
    if (source == request.uri) {
      return {
        status: '301',
        statusDescription: 'Moved Permanently',
        headers: {
          location: [{ value: destination }]
        }
      };
    }
  }

  return {
    status: '302',
    statusDescription: 'Found',
    headers: {
      location: [{ value: 'https://blog.cavelab.dev' + request.uri }]
    }
  };
  
};

It’s pretty basic; it loads regex.json and redir.json as constants. Then it searches through them, if a match it found it returns a 301 Moved Permanently to the destination URL.

If no matches are found, it returns a 302 Found to https://blog.cavelab.dev + request.uri.

Here are is an entry from redir.json:

[
    {
        "source": "/projects/tagged/bascom",
        "destination": "https://blog.cavelab.dev/tags/bascom/"
    }
]

And from regex.json:

[
    {
        "source": "/(wiki|w)/Main_Page$",
        "destination": "https://blog.cavelab.dev/"
    },
    {
        "source": "^/(wiki|w)/?$",
        "destination": "https://blog.cavelab.dev/"
    }
]

Sources in the redir.json must be a direct match with the request.uri, while sources in regex.json must match the regular expression.

Permissions

To have permission to deploy the function to the edge, we first need to add some trust relationships:

  • Navigate to Identity and Access Management (IAM)
  • Go to Roles
  • Click the role for the function you just created, it will be named something like function_name-role-xxxxxxxx
  • Go to the Trust relationships tab
  • Click Edit trust relationship
  • Add edgelambda.amazonaws.com to the Service array, making it look like this:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": [
          "edgelambda.amazonaws.com",
          "lambda.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

Deploy

Alright — time to deploy! Navigate back to the Lambda function we created earlier.

I’m assuming you already have a CloudFront distribution. The origin doesn’t matter, since all requests will be redirected.

On the function page;

  • Click Add trigger
  • Choose CloudFront
  • Click Deploy to Lambda@Edge
  • Leave it on Configure new CloudFront trigger
  • Choose your distribution and cache behaviour
  • Set CloudFront event to Origin request
  • Check Confirm deploy to Lambda@Edge

Deployed!

If you now navigate to the CloudFront distribution and behaviour you selected, you should see it under Edge Function Associations.

Redeploy

If you make any changes to the function, it needs to be redeployed to the edge.

You can do that on the function page:

  • Click the Actions button, top right
  • Click Deploy to Lambda@Edge
  • Select Use existing CloudFront trigger on this function
  • Click Deploy

Domains

The redirect function doesn’t care about the domain, only the path. So whatever domain you use as alternative names for the CloudFront distribution will get redirected.

Remember to add both the naked domain and the www subdomain.