Redirect HTTP to HTTPS and WWW to non-WWW with AWS S3, Cloudfront and Route 53 with a custom domain

Published:
Last modified:

Overview

This guide explains how to have a custom domain with a secure connection, using Amazon services only.

After finishing the guide you will have the following:

  • a static website hosted in Amazon S3
  • HTTPS enabled
  • all your requests redirects to the non-WWW version https://example.com
  • using Amazon’s Cloudfront CDN.
  • using Route 53, Amazon’s domain name server manager

Set up S3

S3 allows you to store and retrieve any amount of data, in particular it makes it easy to set up static websites. The data hosted in S3 is saved as objects and each object can have its custom permissions.

Set up buckets

To have our website hosted in S3, we need to configure two buckets in https://console.aws.amazon.com/s3/buckets/:

non-WWW bucket

One bucket for the naked domain called: example.com. This bucket will hold our static website files.

Turn on the Static Website Hosting for example.com.

Adjust permissions to allow reading your objects, go to the bucket Permissions tab, and then Bucket Policy. We need to configure the Action: s3:GetObject, then if you don’t use the generator, it will look something like:

{
    "Version": "2012-10-17",
    "Id": "Policy1492008478664",
    "Statement": [
        {
            "Sid": "Stmt1492008213599",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::example.com/*"
        }
    ]
}

WWW bucket

Another bucket for the www-version domain called: www.example.com. This bucket will redirect all the requests that start with WWW to the non WWW version.

Turn on the Static Website Hosting for www.example.com.

Go to Properties tabs, Static Website Hosting and select Redirect requests with the following values:

Target bucket or domain
example.com
Protocol
https

Set up CDN

We need a CloudFront Distribution for each S3 bucket, so each website will have its own CDN.

Go to https://console.aws.amazon.com/cloudfront/ and create two distributions with just the default settings but with small changes.

CDN for example.com

The origin setting should contain the custom URL endpoint of our S3 website, avoid selecting the one that the dropdown list suggests for us (REST endpoint) as we need the web site endpoint, not the bucket, it should have the following form: <bucket-name>.s3-website-<region>.amazonaws.com, in this case:

Origin
example.com.s3-website-us-east-1.amazonaws.com
Default Cache Behavior Settings / Viewer Protocol Policy
Redirect HTTP to HTTPS
Distribution Settings / Alternate Domain Names (CNAMEs)
example.com
Do not set the Default Root Object property.
Distribution Settings / SSL Certificate:
Custom SSL Certificate (example.com)
Request or Import a Certificate with ACM

When you create the Certificate include both domains as we are going to use this in the other distribution also:

Add domain names
example.com
www.example.com

And follow the instructions to validate the certificate, then make sure it is selected in the Distribution.

Leave all other setting with their default value.

CDN for www.example.com

Create another distribution for the www.example.com domain with these values:

Origin
www.example.com.s3-website-us-east-1.amazonaws.com
Default Cache Behavior Settings / Viewer Protocol Policy
Redirect HTTP to HTTPS
Distribution Settings / Alternate Domain Names (CNAMEs)
www.example.com
Distribution Settings / SSL Certificate:
Custom SSL Certificate (example.com)
Select our previously created certificate that includes this domain

Set up domains

We are almost there, now it is time to configure the DNS registry with Route 53.

Go to DNS management / Hosted Zones and create a hosted zone example.com.:

Domain Name:
example.com.

In this hosted zone, create a Record Set with these values:

Name:
(leave it empty)
Type:
Alias: Yes
Select the CloudFront distribution corresponding to example.com

Then create another Record Set for www.example.com:

Name:
www
Type:
Alias: Yes
Select the CloudFront distribution corresponding to www.example.com

Verification

All DNS changes and CDN setup take time to propagate, you will have to wait for propagation, then verify it is all working as expected for HTTP, HTTPS and WWW, non-WWW URLs:


$ curl -sI http://example.com | grep -E '(301|Server|Location|X-Cache|HTTP)'
HTTP/1.1 301 Moved Permanently
Server: CloudFront
Location: https://example.com/
X-Cache: Redirect from cloudfront
$ curl -sI https://example.com | grep -E '(X-Cache|HTTP)'
HTTP/1.1 200 OK
X-Cache: Hit from cloudfront
$ curl -sI http://www.example.com | grep -E '(301|Server|Location|X-Cache|HTTP)'
HTTP/1.1 301 Moved Permanently
Server: CloudFront
Location: https://www.example.com/
X-Cache: Redirect from cloudfront
$ curl -sI https://www.example.com | grep -E '(301|Server|Location|HTTP)'
HTTP/1.1 301 Moved Permanently
Location: https://example.com/
Server: AmazonS3

Explanation of commands:

curl
transfer a URL
curl -s
Silent or quiet mode. Don't show progress meter or error messages.
curl -I
(HTTP/FTP/FILE) Fetch the HTTP-header only
grep
print lines matching a pattern
grep -E, --extended-regexp
Interpret PATTERN as an extended regular expression.

Optional: Disable Direct Access to S3

A good practice is also to make your bucket accessible only from your CDN, i.e.: disable direct access to S3 bucket endpoints.

This prevents security issues, and avoid content duplication in search engines.

1. Make S3 bucket private

  • Go to the non-www S3 bucket
  • Select the Permissions tab / Block public access
  • Press Edit button and select the most generic option Block all public access.
  • Save changes.
  • Now go to the Bucket Policy tab.
  • Delete the current Bucket policy.

2. In Coudfront

Set the Root Object

  • Go to the non-www Cloudfront distribution.
  • Press Edit and set the Default Root Object as index.html
    • The object that you want CloudFront to return when a viewer request points to your root URL
  • Save changes pressing Yes, Edit button.

Use Origin

  • Go to Origins and Origin Groups tab.
  • Press Create Origin
  • In Origin Domain Name start to write the name of the non-www S3 bucket and it will appear in the dropdown list, select it. - It will look something like example.com.s3.amazonaws.com
  • In Restrict Bucket Access select Yes, a new set of options appear.
  • In Origin Access Identity, create a new Identity or reuse an existing one.
  • In Grant Read Permissions on Bucket select Yes, Update Bucket Policy so it will generate the appropriate S3 bucket policy for our website.
  • Hit Create button.

Update Behaviour

  • Go to Behaviors tab.
  • Select the default behavior and hit the Edit button.
  • In Origin or Origin Group dropdown option list, choose the origin we have just created in the above step.
  • Save changes with Yes, Edit button.

Access right files

In most cases an additional step is required, this is to correctly handle the access to the S3 bucket files.

So for example, if in your S3 bucket you have an about file, or about/index.html, but in your website you have URLs like /about/, it won’t work. We need to process the request and route the /about/ path to correctly ask for the about file or its index.html. This is done in AWS with a Lambda Edge function.

Lambda@Edge lets you run Lambda functions to customize content that CloudFront delivers, executing the functions in AWS locations closer to the viewer. The functions run in response to CloudFront events, without provisioning or managing servers.

This function does (source):

  • URI paths that end in .../index.html are redirected to .../ with an HTTP status code 301 Moved Permanently. (This is the same as an “external” redirect by a webserver).

  • URI paths that do not have an extension and do not end with a / are redirected to the same path with an appended / with an HTTP status code 301 Moved Permanently. (This is an “external” redirect)

Lambda@Edge Function

We will use the function standard-redirects-for-cloudfront, to install it via the Serverless Application Repository:

  1. Go to AWS Serverless Application Repository

  2. Press the Deploy button to use the application standard-redirects-for-cloudfront.

  3. It opens a description of the app, hit Deploy again to finish deploying it.

  4. After it has been created, locate the button View CloudFormation stack or go directly to the Cloudformation Console

  5. In the Resources tab, locate the AWS::IAM::Role and open the Physical ID, it will open up the IAM console

  6. Go to Trust Relationship tab and choose Edit the trust relationship to allow CloudFront to execute this function as a Lambda@Edge function., set the policy to:

    {
      "Version": "2012-10-17",
      "Statement": [
        {
     	 "Effect": "Allow",
     	 "Principal": {
     	   "Service": [
     		 "lambda.amazonaws.com",
     		 "edgelambda.amazonaws.com"
     	   ]
     	 },
     	 "Action": "sts:AssumeRole"
        }
      ]
    }
    
  7. Go back to the Cloudformation’s Stack Detail page and in the Output tab, locate the key StandardRedirectsForCloudFrontVersionOutput and note down its Value (it will look something like: arn:aws:lambda:us-east-1:XXXXXXXXXXX:function:aws-serverless-repository-StandardRedirectsForClou-XXXXXXXXXXXX:2 ). We will use it in the next steps as this is the ARN (Amazon Resource Name) for the Lambda function that we will use in Cloudfront.

  8. Go back to the CloudFront console, select the non-www distribution (example.com)

  9. Go to the Behaviour tab and edit the default Behavior.

  10. Now we use the Lambda function, in Lambda Function Association select Event Type/Origin Request and enter the Lambda function’s StandardRedirectsForCloudFrontVersionOutput ARN value from the previous step.

  11. Wait for the CloudFront distribution to deploy.

All HTML to index.html

Finally, convert all your non index.html HTML files like an about file or about.html into about/index.html as a directory/index.html scheme.

Before copying your files to the S3 bucket, use this command to convert all the non index.html HTML files files in a public directory, into the above scheme of filename/index.html scheme so they can be accessed from Cloudfront:

find public/ -type f ! -iname 'index.html' -iname '*.html' -print0 | while read -d $'\0' f; do mkdir -p "${f%.html}"; mv "$f" "${f%.html}/index.html"; done

It traverses all the HTML from the public directory, looking for .html files, different from index.html, and creates the filename/index.html.

or as a Makefile recipe, you need to escape $ symbol:

prettyurls:
	find public/ -type f ! -iname 'index.html' -iname '*.html' -print0 | while read -d $$'\0' f; do mkdir -p "$${f%.html}"; mv "$$f" "$${f%.html}/index.html"; done

Conclusion

It may involve more steps than other deployment solutions but you will enjoy the benefits of Amazon Web Services reliability and cost effective services.

Now all the requests to example.com will be served by https://example.com.

References

S3 Website features can be used in conjunction with Amazon CloudFront. However, S3 Website uses a different domain name than regular S3 buckets. In this case, you’ll need to set the Origin Domain Name of your CloudFront distribution’s origin configuration to new.rdegges.com.s3 website us east 1.amazonaws.com.

Uruguay
Marcelo Canina
I'm Marcelo Canina, a developer from Uruguay. I build websites and web-based applications from the ground up and share what I learn here.
comments powered by Disqus

    Articles

    • Redirect HTTP to HTTPS and WWW to non-WWW with AWS S3, Cloudfront and Route 53 with a custom domain

Guide to host a website in Amazon S3 bucket using SSL and a custom non-www domain with Cloudfront CDN.

Clutter-free software concepts.
Translations English Español

Except as otherwise noted, the content of this page is licensed under CC BY-NC-ND 4.0 . Terms and Policy.

Powered by SimpleIT Hugo Theme

·