Redirect www to non-www in CloudFront using Lambda@Edge

Every website should be accessible by using addresses like example.com and www.example.com. On the other hand you should never have a duplicate content by serving the same content on different URLs.

Because of these UX and SEO principles it’s best practice to create a permanent redirection from www.example.com to example.com or vice versa.

When hosting a static website with AWS S3 and CloudFront you must implement the redirection by yourself. There are several ways to achieve this. In this article I’m going to describe how to do it with AWS Lambda@Edge functions.

Create a function that handles the redirection

First create a function that handles all viewer requests and creates the redirection if neccessary.

Go to the AWS Lambda and create a new function with a setting “Author from scratch”. You can name the function whatever you like, but here we’re going to call it redirect_www.

Use Node.js 16.x for the runtime and x86_64 for the architecture.

Change the default execution role for the function by selecting “Create a new role from AWS policy templates” and then choose the “Basic Lambda@Edge permissions” template.

If you miss the previous step, you’ll get an error saying “Your function’s execution role must be assumable by the edgelambda.amazonaws.com service principal” when you try to deploy the function to the Lambda@Edge.

Click “Create function” and write the code that handles incoming requests.

exports.handler = (event, context, callback) => {
  // (1)
  const request = event.Records[0].cf.request;
  const requestHost = request.headers.host[0].value;

  // (2)
  if (requestHost.startsWith('www.')) {
    const response = {
      status: 301,
      statusDescription: 'Moved Permanently',
      headers: {
        location: [{
          key: 'Location',
          value: 'https://' + requestHost.replace(/^(www\.)/, '') + request.uri
        }],
        // (3)
        'cache-control': [{
          key: 'Cache-Control',
          value: 'max-age=31536000'
        }]
      }
    };
    // (4)
    callback(null, response);
  }
  else {
    // (5)
    callback(null, request);
  }
};

Let’s see what’s happening here.

First we read the Host field value from the request headers (1). Then we determine if the hostname begins with the string “www.” (2). If it does, we create a custom response with a HTTP status 301 Moved Permanenly and set the Location header to the URL without the “www.” prefix.

We also set the cache-control header to the maximum value (3).

At last we call the callback function with our custom response object (4) or with the original request object (5), depending on if the hostname contains “www.” or not.

Publish a new version of the function

Click “Deploy” and then choose “Publish new version” from the Actions menu.

Deploy the function to Lambda@Edge

Select “Deploy to Lamda@Edge” from the Actions menu.

Select “Configure new Cloudfront trigger”.

Select your CloudFront distribution and cache behavior.

For the CloudFront event choose “Viewer request” as we want to trigger this function for incoming viewer requests in the CloudFront.

Click “Deploy”.

Configure CloudFront and Route 53

The CloudFront distribution using our redirection function should have an alternate domain name defined for both domain names, with www prefix and without it. The distribution’s SSL cert must include both domain names too.

In Route 53 you should create “A” record for both domain names and define them as aliases to the CloudFront distribution.