Hosting an HTTPS static website on S3 with help from Cloudfront

Every year, I like to re-design my personal website (https://www.lukeduncan.me) to learn new things, update my work and refresh for the new year. For the better part of three years now I have hosted my site on AWS S3 where I simply update my DNS records for my domain to point to my public bucket URL. The site has been super easy to update, manage and overall it’s been great!

Now that the web is making a huge deal of SSL (which it should be), I decided my project for this new year would be to get SSL working alongside my personal domain and static website. Basically my website was http://www.lukeduncan.me — And I want it to be https://www.lukeduncan.me.

This article will cover the exact steps I took to get my site running and working with SSL.

For this tutorial we will be using:

  • AWS S3
  • AWS Cloudfront
  • AWS Certificate Manager
  • Static website (I use Middleman (https://middlemanapp.com/) to build my static websites)

1) Upload your website to AWS S3

This first step could probably be a whole other article, but basically upload your website to an AWS S3 bucket. Hopefully you already have your website set up to host through S3 but if not, there are plenty of articles around the web showing you how to set that up with your domain.

David Walsh does a great job in his article here: https://davidwalsh.name/hosting-website-amazon-s3 (ignore the Surreal CMS stuff unless it interests you)

Some helpful hints:

  • Make sure your S3 bucket is named after your website e.g. my bucket is called www.lukeduncan.me
  • Use a static site generator to make it easier to build your website and then utilize deployment gems e.g. I use this https://github.com/fredjean/middleman-s3_sync which allows me to quickly deploy changes from my middleman site to my s3 bucket
  • Make sure that the bucket itself is public

2) Generate the SSL certificate using AWS Certificate Manager

To make this easier to follow, I’ll break everything from here on out into actionable steps that you should be following.

  • Access Certificate Manager from your AWS console
  • Click on the “Request a certificate” button
  • Request a public certificate
  • Add your domain name e.g. mine was www.lukeduncan.me
  • Next your going to have to validate your certificate somehow to make sure AWS knows you own the domain. I went with the email validation because I have control of the emails that hit my domain, but you can easily do this using DNS records as well
  • Once you have done this, and confirmed the certificate, you are all good to go!
yay!

3) Create a distribution via Cloudfront to host our website via HTTPS

So now we’re going to get Cloudfront going alongside the certificate we just created.

  • Your going to want to create a new distribution. Simply click on the “Create distribution” button to get started
  • Your going to have option between selecting between creating a distribution for web or rtmp. Select web
  • Next the first input will be Origin Domain Name. Click on that and it will give you a dropdown of S3 buckets that you have. Select your website bucket. In my case it would be: www.lukeduncan.me.s3.amazonaws.com
  • The next option we are going to touch is called Viewer Protocol Policy. Some people choose to redirect HTTP to HTTPS. But I just use the HTTP and HTTPS option.
  • Under Alternate Domain Names, your going to want to put your website domain. e.g. in my case www.lukeduncan.me and lukeduncan.me — both of these.
  • Now we are going to select the SSL certificate we made earlier. Under SSL certificate section, select custom SSL certificate. And once the dropdown shows up, select your domain.
  • Under the default root object (which I think is already handled by S3) but just in case, add index.html or whatever your homepage is for your website.
  • And finally — click Create Distribution.
  • It will take 15 minutes for Amazon to distribute this and successfully deploy. Before we take the next step your distribution should look like this below (I removed ID and domain name just for sake of):

4) Connect your domain name to the Cloudfront distrubution using CNAME records

Now if any of you have hosted an S3 bucket as your website before, you know you could just set your CNAME record as the bucket URL e.g. the one shown above — www.lukeduncan.me.s3.amazonaws.com

But — in order for SSL to take effect, we need to use the Cloudfront distribution that is taking in the S3 contents and distributing it correctly. For my domains, I use Namecheap but this can be used for any domain host.

  • Your going to need your domain name from the Cloudfront distribution. It will look something like this (d1shfic93mswn.cloudfront.net)
  • Create a new CNAME record under your domain
  • Make the host WWW, and the value the Cloudfront distribution domain name. I always like to set the TTL to automatic but that’s up to you
Heres my CNAME record in Namecheap for my distribution

And MOOLAH BABY.

You should be all set to go. Wait a few minutes before this is up and working but you should now be able to go to https://yourwebsite.com and now you have SSL on your own static website!

Let me know if you have any questions by asking me on Twitter (@luke__duncan) or by emailing me luke.will.duncan@gmail.com

BONUS CONTENT:

Now some of you are going to run into problems where Cloudfront caches your files, images, stylesheets, javascript — basically everything on your website. So when you make changes and push new content to your S3 bucket, they aren’t going to be reflected on your website.

To push through your changes, your going to have to invalidate files from your Cloudfront distribution. Go to your distribution and then click on the tab “invalidations”. From there your going to input the file name that you want to invalidate that has new stuff in it.

E.g. in my case, if I wanted to invalidate the CSS file I would create an invalidation and add this line in it:

/stylesheets/site.css
/stylesheets/*

I like adding the * route just in case. And then when I click on create, it should take around 10 minutes for Cloudfront to invalidate that file, and go and get the new one from my S3 bucket.