Redirecting non-www to www website in AWS CloudFront

This is standard stuff for anyone familiar with Apache and mod_rewrite but is a little complicated with static sites using S3 and CloudFront.

In order not to dilute your SEO it’s important to choose whether the “official” version of your site is hosted at www.example.com or just example.com, and then stick to that. Which you choose isn’t particularly important (there’s lots of discussion out on other forums if you’re interested), but once you’ve chosen, you need to set things up so people entering the other version in their browser end up being redirected to the correct one.

In my case I chose to standardise on the “www” version of the site.

Initially I thought I could extend the CloudFront Edge Lambda function I wrote for handling default files in S3 subdirectories, but surprisingly there is no field in the CloudFront request object that tells you what the domain of the original request was.

Here’s a full example request object from CloudFront — notice no mention of whether the request was for www.example.com/foo?q=1 or example.com/foo?q=1

{ 
“Records”: [
{
“cf”: {
“config”: {
“distributionDomainName”: “abcd1234.cloudfront.net”,
“distributionId”: "BCDE2345”,
“eventType”: “origin-request”
},
“request”: {
“body”: “”,
“bodyEncoding”: “base64”,
“bodyTruncated”: false,
“clientIp”: “1.1.1.1”,
“headers”: {
“accept-encoding”: [ { “key”: “Accept-Encoding”, “value”: “gzip” } ],
“host”: [ { “key”: “Host”, “value”: “www-example-com.s3.amazonaws.com” } ],
“upgrade-insecure-requests”: [ { “key”: “upgrade-insecure-requests”, “value”: “1” } ],
“user-agent”: [ { “key”: “User-Agent”, “value”: “Amazon CloudFront” } ],
“via”: [ { “key”: “Via”, “value”: “2.0 cdef3456.cloudfront.net (CloudFront)” } ],
“x-forwarded-for”: [ { “key”: “X-Forwarded-For”, “value”: “1.1.1.1” } ]
},
“method”: “GET”,
“origin”: {
“s3”: {
“authMethod”: “none”,
“customHeaders”: {},
“domainName”: “www-example-com.s3.amazonaws.com”,
“path”: “”
}
},
“querystring”: “q=1”,
“uri”: “/foo”
}
}
}
]
}

While I think that it should be possible to handle all the functions of mod_rewrite within a single CloudFront Edge Lambda function, without the requested host I don’t know how to go about it. So here’s what I did instead.

  1. Create another CloudFront distribution for the non-www domain
  • Point it to the same S3 bucket as the www one (this content is never served but you have to choose something)
  • Set the alternate domain name (CNAME) to example.com (note that if, like me, you had example.com andwww.example.com set up on an existing distribution, you will need to remove example.com from that before you make it a CNAME for your new one — a few mins downtime for example.cominvolved)
  • Set the distribution to redirect http to https
  • Set it to forward all query strings (so that they are passed to the Lambda and can be added to the redirect)

2. Create a Lambda function to redirect all requests to www

  • Remember to create the function in us-east-1 so you can deploy to CloudFront Edge
  • Note that this function also adds a trailing slash to directories if needed in order to save an additional redirect on the www service

3. Set the DNS for example.com to point to this distribution

  • Not all DNS services can handle this, but if you’re using Route 53 you can do this using an Alias