HTTP/HTTPS Redirects with CloudFront and Lambda@Edge

Published on 09 August 2021

Static hosting on S3 is a really cost effective way to have a website without having to pay for a server. When you put it behind CloudFront, you get all the benefits of faster performance by content distribution, as well as the ability to use a free HTTPS certificate. That is on top of HTTP to HTTPS redirection for better security (and to keep search engines happy). The problem I had is that my static content generator doesn't code full links for some things, so /tags should actually go to /tags/index.html. When you stick CloudFront in front of an S3 bucket configured to server static content, that functionality breaks.

error

The solution? Use Lambda@Edge with CloudFront to do a redirect.

The Lambda Function

You need to create the Lambda function in US-EAST-1, regardless of where you or your content is located. So you can create a really simple Lambda function there, in the language of your choice. I did mine in Python. Basically, I just want to return the destination URL, that I want to send traffic to, rather than getting the error message. So this one is going to redirect to tags/index.html, and then I need to configure when that gets used.

        def lambda_handler(event, context):

        # Generate HTTP redirect response with 302 status code and Location header.

            response = {
                'status': '302',
                'statusDescription': 'Found',
                'headers': {
                    'location': [{
                        'key': 'Location',
                        'value': 'https://www.heywoodonline.com/tags/index.html'
                    }]
                }
            }

            return response

So that can be used as a Lambda function, but I also needed to associate an IAM role with it too. The role should have permissions to log to and access CloudWatch logs, and also have lambda and lambdaedge as trusted entities. If you create a new role while creating the function, you can choose a lambda@edge template for that. Additionally, I needed to create a version as you need to refer to a specific version (not an alias, that I could figure out) for CloudFront.

CloudFront Configuration

Once done, I needed to then create a new behaviour in the CloudFront distribution. So in distribution, I went to the Behavior tab, and clicked "create behavior". Then it was just a matter of setting the path pattern "/tags", choosing the origin (my bucket).

CF1

Then I went down to the bottom to the "Fuction Associations" part, which is where the magic happens. Here it was just a matter of choosing the correct behaviour. In this case, I want viewer request (so for any request that comes in that matches the path pattern), and then just paste in the ARN of my function (including the version!).

CF2

And that is really about it. After that, I gave CloudFront a few minutes to re-deploy, and then tested, and all good!

fixed!

Troubleshooting

As always, you can check your code by running it in VSCode or similar, and you can always test it in the Lambda console and look for errors there. Remember to check the CloudWatch logs too.

comments powered by Disqus