Making an AWS static website secure

So there I was, patting myself on the back for making an Azure static website secure (with all the right headers, natch), when I gave myself a quick nod: yep, let’s do the same for this other static website, one that’s hosted on Amazon S3. Morceau de gâteau!

Please, please, please, can I go back in time to stop myself? What a lengthy ordeal, a flippin’ slog. Sisyphus had it easy.

Let’s enumerate what you should do, in the right order (rather, than what I did, which was all messed up).

Get your static website up and running

This is the easy part. In fact, I’d done it ages ago for WhoIsThisJulian.com. As it happens, the complete steps to do this are pretty easy to follow even in the official documentation. You’ve bought your domain. You then set up two buckets in Amazon S3 (one for the unadorned domain name, such as example.com, and one for the www-adorned version, that is www.example.com). You make the first one public, and then you set things up in Route 53 to properly point to these buckets.

Already you’re starting to sink into the Amazon way of referring to these things. S3 stands for Simple Storage Service and is the way you, er, store things in the cloud with Amazon. Route 53 is a web service that, well, routes requests to web apps and sites (among other things). It’s a DNS service in essence. And why “53”, I don’t know (unless it’s because it looks like S3).

Fix your HTML to help make the site secure

The reason? I found doing this to be a right royal pain in the neck after I’d set up the basic HTTPS support. (Hint: it involves caching from somewhere I wasn’t expecting.) So, in no particular order:

Test your changes to make sure everything still works the way you want, because now we hit the big time.

Set up a new CloudFront distribution

This is where it gets hairy. CloudFront, to quote Amazon, “is a global content delivery network (CDN) service that securely delivers data, videos, applications, and APIs to your viewers with low latency and high transfer speeds.” The important thing to note here is that we shall be changing how people get to your site: they will instead go through CloudFront to get to it. If you like, it’s inserting itself between your viewers out there and your site hosted on Amazon S3.

For this part I made use of a walkthrough on Medium, recommended by an old mate, Bryan Slatner. The big change for me from what it recommended is that I’d bought my domain from GoDaddy, with whom I have an email plan. I already had my MX records pointing to their servers, and didn’t have to do any of that Amazon SES stuff from the walkthrough. (That would be Amazon’s Simple Email Service.)

So, step 1 then is to create a new web distribution on CloudFront. The Origin Domain Name is simple: it drops down a list of your S3 buckets. Choose the non-www one for your domain. I ignored the rest of the Origin Settings section.

Onto the Default Cache Behavior Settings section. Here I chose Redirect HTTP to HTTPS, and not HTTPS Only, as suggested in the walkthrough. The Object Caching option got me worked up (I tried several different settings), but to be honest you can leave it alone. Leave all the other options as default.

For the Distribution Settings section, enter your domain names (for example, example.com and www.example.com – do both) as an Alternate Domain Name (CNAMEs).

Now for the fun part. The next question is about the SSL Certificate. Choose the Custom SSL Certificate option and then click on the Request or Import a Certificate with ACM button. (ACM? AWS Certificate Manager.) Why? Because you are going to…

Create an SSL Certificate from Amazon

Clicking on that button opens up a new tab/window and takes you to the Request a certificate page. You have to fill in the domain names you want this new certificate to apply to. THIS IS WHERE I WENT WRONG TO BEGIN WITH, so pay attention. Enter your domain name twice (as it were), first as example.com then add *.example.com as the second ‘name’ by clicking on the Add another name to this certificate button. In essence, you will have two edit fields filled in before you press the Next button.

The next screen asks you whether to validate through DNS or email. I chose email. As it turns out, this was where that Medium walkthough led me off the beaten path. ACM will email a whole bunch of addresses that are @example.com (for example, postmaster@example.com, etc), but it will also email the owner of the domain according to WHOIS. Fine by me: I use my GMail address for that purpose. I would just get the email in my usual inbox. (So, you don’t have to set up those email addresses and MX routes if you don’t want to.)

After a very short time, you’ll get the validation email. This email has a link to click to approve the certificate, so click on it. You get sent to a page with a big button saying I Approve. So click on that. Boom, done. you have a free SSL Certificate from Amazon. Select and copy that long ARN value. (ARN: Amazon Resource Name.)

So, now, go back to your open page where you were creating a CloudFront distribution.

Finish your CloudFront distribution

…and paste that ARN into the field under the Custom SSL Certificate option.

Next, and this one I managed to forget, causing lots of Access Denied errors as I tried to work out what wasn’t working, is to set the Default Root Object for the site. For WhoIsThisJulian.com it’s default.html, but on another site I’ve since secured, it was index.html. Yes, I know that a viewer never has to type those default page names into the address bar, but CloudFront needs to know.

Leave the rest as is, and click on the Create Distribution button right at the bottom of the page. You get redirected to your CloudFront Distributions page with your new distribution showing a Status of a rotating pair of arrows and In Progress.

Sit back, go make a coffee, read the news, do the Sudoku puzzle, take the dog out for a walk, play some tennis, just occupy yourself for some looooong period of time. (I’ve read that it takes 8-10 minutes, but that must have been in the days when no one used CloudFront. For me, it’s easily 20-25 minutes, or more. That’s why I got so wackily frustrated with this process: if something didn’t work, I’d change something in the distribution, and bam another half hour of my life was gone.)

Eventually, that rotating arrows indicator will disappear and be replaced with Deployed. Yay! Except…

Check your Route 53 settings

On the line for your newly created distribution, there will be a special CloudFront Domain Name. It’ll be a random looking name of the form d012345abcdef.cloudfront.net. Select and copy it. (You can, if you want to, open up a new tab in your browser and navigate to that URL to see your site in all its secure glory.)

Switch over to the Route 53 dashboard and open up the Hosted Zone for your site. You’ll have several different DNS record sets visible, but there are two important ones that’ll need changing: two A record sets, one for your plain domain name (e.g., example.com) and one for the www version. You’ll see that the ALIASes point to some URL on amazonaws.com. We now have to change that to point them to that special unique CloudFront URL. The unadorned domain name (example.com) must have an A record set point to that new URL, and the www version must have a CNAME record set pointing to the same place. You may find that this has already been done by setting up the CloudFront distribution. (I’m unsure on this point: the first time I did this, it had been done already, the second time, I had to do it myself. Your mileage may vary.)

Once you’ve saved those changes, your site should be reachable and, more importantly, secure.

So we’re done?

No. Remember last time we had to change the security headers returned from your site? Well, we have to do something similar here on Amazon. But that will have to wait until Part 2.

Locks on Bridge - banner

Loading similar posts...   Loading links to posts on similar topics...

3 Responses

 avatar
#1 Matt said...
20-Apr-18 8:49 PM

I think the 53 is for the port that DNS runs on...

julian m bucknall avatar
#2 julian m bucknall said...
21-Apr-18 5:20 PM

@Matt: D'oh! Of course! Nice one.

 avatar
#3 Ivan Nikitin said...
12-Sep-18 4:08 AM

Google bot sporadically fails to download CSS from CloudFront and this damages SEO rankings.

Check if you see any mobile errors in Google Search Console after moving the webiste to CloudFront. I have a static website also hosted on S3/CF and Google bot fails to download external resources from time to time. Looks stupid, but this causes sporadic "mobile usability" errors. Google bot fails to download CSS, website without CSS looks bad on mobile and Googlebot thinks your website isn't optimized for mobile. You may see these errors in Google Search Console. They appear and disappear but they damage search rankings A LOT.

CF logs show no errors from Google bot. It seems that GET requests from Google do not reach the CloudFront servers from time to time. I still have no explanation why. Pingdom never reported any downtime issues. The problem appeared in December 2017.

I tried this and that but struggled to solve the problem for several months. In the end I inlined all JS and CSS styles into HTML. Once Google indexed updated pages and mobile error count went to 0, my rankings went up overnight. Average daily visits from Google went up from ~600 to ~900 in a single day. Search Console started to show 0 errors on Aug 1 and the same day traffic increased considerably.

Leave a response

Note: some MarkDown is allowed, but HTML is not. Expand to show what's available.

  •  Emphasize with italics: surround word with underscores _emphasis_
  •  Emphasize strongly: surround word with double-asterisks **strong**
  •  Link: surround text with square brackets, url with parentheses [text](url)
  •  Inline code: surround text with backticks `IEnumerable`
  •  Unordered list: start each line with an asterisk, space * an item
  •  Ordered list: start each line with a digit, period, space 1. an item
  •  Insert code block: start each line with four spaces
  •  Insert blockquote: start each line with right-angle-bracket, space > Now is the time...
Preview of response