How I Learned to Love Redirects: Lessons from Rolling out CloudFront on top of a Static S3 Site

Today, I finally got around to migrating my website to use HTTPS and SSL - something that will go under-the-hood to nearly everyone, but is becoming more and more important to do as a foundational internet thing.

Here’s a blurb from Namecheap in response to the question “Do I need an SSL certificate?”

We’ll just say it: yes. Your website needs any SSL certificate If you’re asking for any personal information. But that’s not all there is to it. Search engines are cracking down on perceived ‘non-secure’ websites. Any websites without the SSL certificate will remain http while those with encryption will show https in users’ browsers. Chrome, Firefox and other browsers have began issuing warnings that non-https sites are insecure. Additionally, Google recently announced SSL is a ranking signal, so unless you have SSL your site will be harder to find, impacting on your traffic and revenue.

While my site doesn’t handle any user logins, transactions, or other actions users might need to keep protected, a desire to follow best engineering practices has finally outweighed my reticence of making structural changes to the site (you never know what will unexpectedly take down the whole thing!)

Getting this Working in AWS

My existing website is a static website built by Hugo and deployed on AWS S3 - a terrific solution for cheap, serverless hosting. However, AWS S3 hosting currently does not support SSL certificates, and thereby HTTPS connections. Because of this, we need to plop one more bit of computing on top of our stack - an AWS CloudFront distribution.

CloudFront is Amazon’s content delivery network (CDN) offering, which allows internet content to be served more quickly to end users by optimizing the distribution of data across many data centers around the world. CloudFront also allows users to secure their content with SSL certificates - exactly what we need to get HTTPS live for the site.

It’s not super hard to get a CloudFront distribution up and running - there are a ton of good guides - so just wanted to share some quick notes on the setup below:

Create an SSL Certificate to Use

  • Create a certificate covering both domains (conormclaughlin.net and www.conormclaughlin.net)
  • Must be in same region as S3 bucket and CloudFront distribution

Set up a Cloudfront Distribution

  • Source the distribution from the S3 bucket containing the website
  • Configure the distribution CNAMES to cover the website and aliases
    • I set conormclaughlin.net and www.conormclaughlin.net
  • Configure the Viewer Protocol Policy to Redirect HTTP to HTTPS

Route53

  • Point DNS A Records for the domains (conormclaughlin.net and www.conormclaughlin.net) to the CloudFront Distribution

Issue - The Site is Now Partly Available, but Broken

At this point, I thought I would be done! SSL cert active, connections via HTTPS, etc. However, you can see in the screenshot below that the site is clearly not loading properly!

Note that in addition to the funky style sheets on the primary page, I also was not able to navigate to other links on the page - getting an accessdeniedaccess error message from CloudFront each time.

Broken Site

Two Critical Fixes

Hugo Setup

We need to modify the baseURL field in the config.toml file so that our static site sets URL paths in a way that is generalized from the type of HTTP/S service being used.

baseURL = "http://conormclaughlin.net/"

Remove the protocol prefix to match the following, to support HTTP/HTTPS redirects.

baseURL = "//conormclaughlin.net/"

CloudFront Object Redirects

Most importantly, we need to fix a huge issue with CloudFront’s default behavior: it does not handle URLs of directories or subpages well.

Essentially, Cloudfront does not route do any work to reconcile URL paths to the target HTML files they point at:

  • On my site, that means that loading https://conormclaughlin.net/categories/ will result in an accessdeniedaccess error message
  • While loading https://conormclaughlin.net/categories/index.html works perfectly

Fortunately there is a pretty clear fix to this issue! Here’s a really good description of exactly what needs to be implemented:

CloudFront Function Setup

Create and Deploy CloudFront Function

Fortunately for us, we can add a small function to CloudFront which can help us dynamically re-write the URL requests so that they reconcile to the appropriate index.html files in each path.

function handler(event) {
    var request = event.request;
    var uri = request.uri;
    
    // Check whether the URI is missing a file name.
    if (uri.endsWith('/')) {
        request.uri += 'index.html';
    } 
    // Check whether the URI is missing a file extension.
    else if (!uri.includes('.')) {
        request.uri += '/index.html';
    }

    return request;
}

CloudFront Function Setup

Modify CloudFront Distribution Behavior

Once the function is set up, we just need to tweak our CloudFront distribution to add a Viewer Request function, so that all views will trigger our “redirect_to_index_html” function.

CloudFront Function Setup

Now everything should load perfectly on the deployed version of your site!