Wildcard Domain Redirect using AWS CloudFront Functions

May 12, 2021 - 5 min read

If you have more than a single domain for your site the simplest option would be to use your registrars redirect feature. But why take the easy route when you can do it the fancy way?

In this post I’ll guide you through the process of using AWS CloudFront Functions to not only redirect your apex domain but to also preserve both subdomain and path. Here are two examples:

Redirect just the apex domain: 😃
https://example.comhttps://example.net

Redirect and preserve subdomain and path: 😲
https://www.example.com/bloghttps://www.example.net/blog

Redirect subdomain that doesn’t even exist:_ 🤯
https://some-crazy-subdomain.example.comhttps://some-crazy-subdomain.example.net

CloudFront Functions is not like Lambda@Edge

CloudFront Functions is for super simple tasks only. Unlike Lambda@Edge which gives you a full Node.js (or Python) environment you only get access to a slim ES5 compatible runtime. This means that you actually cannot use “modern” features such as const and let or object destructuring. Additionally can’t even access the request/response body and you’re only able to execute code for maximum of 1ms.

But on the upside setting up a function is significantly easier and they get executed in all of edge locations rather than only in a few regions.

You can read the full introduction and comparison on the AWS Blog.

Creating the function

First visit the Functions page in your CloudFront console and click Create function.

Then enter a name for your function - I chose domain-redirect.

Screenshot of function creation screen

After you’ve created the function paste the following code (don’t worry I’ll explain it in a moment) and press save.

function handler(event) {
  var request = event.request;

  var splitHost = request.headers.host.value.split('.');
  splitHost[splitHost.length - 2] = 'example';
  splitHost[splitHost.length - 1] = 'com';
  var adjustedHost = splitHost.join('.');

  var response = {
    statusCode: 302,
    statusDescription: 'Found',
    headers: {
      location: {
        value: `https://${adjustedHost}${request.uri}`,
      },
    },
  };

  return response;
}

The required format for CloudFront Functions looks like the one in the code snippet. Strangely they differ from regular Lambda and Lambda@Edge functions when it comes to the statusCode field name and the way that headers are specified. As I already explained the idea of this function is to update just the target host address to include another apex domain. To achieve we redirect the user using the Location header and an appropriate status code. Usually you use either 301, 302, 307 or 308 for redirects you read more about these status codes on MDN. Depending on your use case you might want to change the code to use your status code of choice.

To change the apex domain we first extract the domain which was accessed from the Host request header. Then because domains always consist of at least two parts separated by dots we take advantage of this and use the split method to create an array that contains all the segments. Next we replace both the second last and last segment with the ones corresponding to our target domain. Remember to adjust this to redirect to your own domain! Finally we join the array back into a single string using the join method and a dot as separator.

Regarding the path (found in request.uri in function code) we don’t change it and simply append it to our adjusted host. You might have also noticed that I hard-coded the protocol to be https. I chose to do so because every website in 2021 should use it by default anyways and if yours is not then you are clearly doing something wrong and need to change that immediately!

Linking the function

Now you have to associate the function with a distribution and have it trigger on Viewer Request.

If you haven’t created a distribution yet create one as explained by the documentation using a custom domain name while also making sure to add a wildcard subdomain like *.example.com. Note that most settings are irrelevant as they will be bypassed by the function. Depending on your domain registrar you may not be able to add a CNAME record for the apex domain. In that case you’ll have to use another DNS provider like either AWS Route53 which directly integrates CloudFront or one like Cloudflare which allows you to enter a CNAME at apex level and will then flatten it.

In either case associate the function with the distribution and have it trigger on Viewer Request.

Screenshot of function creation screen During the initial setup

Screenshot of function creation screen Afterwards through the functions interface

Limitations

Due to the way that wildcard SSL certificates work you will face an error whenever you try to access a multi-level subdomain like https://sub.sub.example.com through https. If you wanted to secure that subdomain as well you could either add it to the certificate specifically or add the subdomain above as a wildcard. In this example this would be *.sub.example.com.

You might have noticed that I only considered domains with one ending segment. If you were to apply this function for example to a .co.uk domain it would actually replace the apex domain name itself.