A recurring question among website owners is whether to choose non-www or www URLs. (source)
As I’m considering myself somewhat of a minimalist, I’ve decided I want to serve my website on the non-www domain.
In this post I’ll show you how to redirect viewers from the www domain to the non-www domain using Lambda@Edge.
Lambda@Edge
Lambda@Edge is an extension of AWS Lambda, a compute service that lets you execute functions that customize the content that CloudFront delivers. https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-at-the-edge.html
There are four CloudFront events that can trigger a Lambda function to execute: viewer request, origin request, origin response and viewer response.
Image from AWS documentation
The right place to implement our redirect is the viewer request event as we want to redirect the user before CloudFront even checks if the requested object is in the CloudFront cache.
Add the redirect
I’m using the Serverless Framework to provision the CloudFront distribution that is serving this blog. The framework supports the CloudFront events as a trigger so adding a function that does the redirect is relatively easy.
I have configured two A records in Route53 that route traffic to the CloudFront distribution, one for the www domain and one for the non-www domain.
We can check the Host
header to determine whether a viewer came in through the www domain or the non-www domain.
If they did come in through the www domain, we return a response with status code 301
and the Location
header set to the same url the user came in with except for the www.
part stripped. If they didn’t we do not touch the request and pass it on.
In the end, the function looks like this:
def handle_event(event, context):
request = event["Records"][0]["cf"]["request"]
headers = request["headers"]
host_header = headers["host"][0]["value"]
if host_header.startswith("www."):
uri = request["uri"]
qs = request["querystring"]
domain_without_www = host_header.strip("www.")
response = {
"status": "301",
"statusDescription": "Moved Permanently",
"headers": {
"location": [{
"key": "Location",
"value": f"https://{domain_without_www}{uri}" if not qs else f"https://{domain_without_www}{uri}?{qs}"
}]
}
}
return response
return request
I’ve then configured the function in my serverless.yaml
like this (irrelevant parts left out):
service: cloudfront
provider:
name: aws
region: us-east-1
runtime: python3.8
custom:
domainName: <myDomainName>
functions:
viewerRequest:
handler: src/functions/viewer_request/handler.handle_event
events:
- cloudFront:
eventType: viewer-request
origin: http://${self:custom.domainName}.s3-website-eu-west-1.amazonaws.com
resources:
Resources:
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Aliases:
- ${self:custom.domainName}
- www.${self:custom.domainName}
Origins:
- Id: ${self:custom.domainName}
DomainName: ${self:custom.domainName}.s3-website-eu-west-1.amazonaws.com
CustomOriginConfig:
OriginProtocolPolicy: http-only
DefaultCacheBehavior:
TargetOriginId: ${self:custom.domainName}
ViewerProtocolPolicy: redirect-to-https
AllowedMethods:
- GET
- HEAD
- OPTIONS
ForwardedValues:
QueryString: true
After deploying this I can also see that the Lambda function is associated with the cache behavior in the AWS console.
Let’s test it out:
$ curl -iL https://www.jurriaan.cloud
HTTP/2 301
content-length: 0
location: https://jurriaan.cloud/
server: CloudFront
date: Tue, 29 Dec 2020 11:04:14 GMT
x-cache: LambdaGeneratedResponse from cloudfront
via: 1.1 3c01812e357a7900959ea67a1c5782ad.cloudfront.net (CloudFront)
x-amz-cf-pop: AMS50-C1
x-amz-cf-id: QJ-TK3kaLkJ1BMgzxP-LduIHy7efkpWu2Um9LJL0VYOSStLpf90CeQ==
HTTP/2 200
content-type: text/html
content-length: 4301
x-amz-id-2: OeaCkgGlHbaDO6VHVHSghVFysdRLzsPlJ7LcVPCEc658vQIPQ6CyU6PsSOFy/VUW8XSTiXQvVtE=
x-amz-request-id: 6162F12678845FAB
last-modified: Mon, 28 Dec 2020 14:35:41 GMT
server: AmazonS3
date: Tue, 29 Dec 2020 11:04:15 GMT
cache-control: no-cache
etag: "84107066793acaa28d5ffbadc3fec382"
x-cache: RefreshHit from cloudfront
via: 1.1 9fce949f3749407c8e6a75087e168b47.cloudfront.net (CloudFront)
x-amz-cf-pop: AMS50-C1
x-amz-cf-id: ATiO8jCE27cBONXbw4VPcHZ8R_-tEgCdtNMBPN4XZmyl8TkFPJYh9Q==
It works for URLs that contain a path as well:
$ curl -iL https://www.jurriaan.cloud/manage-python-dependencies-in-serverless-projects-with-serverless-layers-plugin/
HTTP/2 301
content-length: 0
location: https://jurriaan.cloud/manage-python-dependencies-in-serverless-projects-with-serverless-layers-plugin/
server: CloudFront
date: Tue, 29 Dec 2020 11:06:31 GMT
x-cache: LambdaGeneratedResponse from cloudfront
via: 1.1 dd133741afef09b02f3e6afd7cb39f40.cloudfront.net (CloudFront)
x-amz-cf-pop: AMS50-C1
x-amz-cf-id: 21uTcM40vvO2yy9clgfcdUThH_26nr4em2zE874wXDW23sn1cP74cQ==
HTTP/2 200
content-type: text/html
content-length: 19357
x-amz-id-2: 9F/cdUB9VxX4RhacXD5+nEk4QQtO3ZAtBR+B/v6RHXtbjgdC3DvjPwFZo5DbzX2DUXfynqyf6XM=
x-amz-request-id: 67F3725866AE73EB
last-modified: Mon, 28 Dec 2020 14:35:41 GMT
server: AmazonS3
date: Tue, 29 Dec 2020 11:06:33 GMT
cache-control: no-cache
etag: "b8c913de37afe87f7662626e09def0f8"
x-cache: RefreshHit from cloudfront
via: 1.1 52102486f97ad6ff39f81538f01349ab.cloudfront.net (CloudFront)
x-amz-cf-pop: AMS50-C1
x-amz-cf-id: qVtHd8fPzsierejaZF-CfjvTONvckVRy0HwbO29Pevc4AXXau4Qhjg==
And we can also confirm that requests to the non-www domain aren’t redirected:
$ curl -iL https://jurriaan.cloud/manage-python-dependencies-in-serverless-projects-with-serverless-layers-plugin/
HTTP/2 200
content-type: text/html
content-length: 19357
x-amz-id-2: 9F/cdUB9VxX4RhacXD5+nEk4QQtO3ZAtBR+B/v6RHXtbjgdC3DvjPwFZo5DbzX2DUXfynqyf6XM=
x-amz-request-id: 67F3725866AE73EB
last-modified: Mon, 28 Dec 2020 14:35:41 GMT
server: AmazonS3
date: Tue, 29 Dec 2020 11:08:07 GMT
cache-control: no-cache
etag: "b8c913de37afe87f7662626e09def0f8"
x-cache: RefreshHit from cloudfront
via: 1.1 552d1a24616d6b8d6e3fbbdf18a54b6a.cloudfront.net (CloudFront)
x-amz-cf-pop: AMS50-C1
x-amz-cf-id: nmPyUhi99-Pt8McBCtgqa1tFxRTbchbOB0XAlS5eF-hD5OklS9_-6g==
🎉
Conclusion
Lambda@Edge is a perfect place to apply this redirect of www to non-www. It can be used for lots of other things as well. Make sure to take into account the requirements and restrictions when using Lambda@Edge for more complex things.
Top comments (0)