Edd Mann Developer

Setting up GitHub Pages HTTPS Custom Domains using CloudFront and Lambda@Edge

We recently wished to switch over our sites hosted on GitHub Pages to be soley HTTPS. However, although you are able to supply a custom domain or support HTTPS traffic you are not able to do both. In this article I would like to guide you through the process of how we went about achieving this using CloudFront, Route 53 and Lambda@Edge.

Setting up the CloudFront Distribution

To get around the fact that we can only route plain HTTP traffic using custom domains within GitHub Pages, we decided to add a CloudFront distribution in-between the client and origin. Doing so allowed us to supply our own SSL certificate and custom domain secure connection. Below is a high-level diagram depicting the approach we took.

Architecture

We first setup a new distribution which was associated with a certificate supplied by AWS Certificate Manager.

Distribution Settings

Following this we created a new origin which routed traffic to the specified *.github.io domain. We ensured that only HTTPS via TLS 1.2 was used for requests to the origin server for security concerns.

Distribution Origin

With this origin in-place we then created a default cache behaviour which enforced HTTPS traffic (redirecting HTTP traffic if found).

Default Cache Dehaviour

Handling Github Page Redirects

One issue we faced with routing traffic through CloudFront was if GitHub attempted to return a redirect response to the client (i.e for missing trailing slashes). As GitHub is unaware of the custom domain, it attempts to route traffic to the *.github.io domain. To get around this we took advantage of a Origin Response Lambda which simply replaced any such responses Location header with the desired custom domain.

To ensure that CloudFront had access to this Lambda we had to define it within the us-east-1 region. Using the simple handler below we published a new version of the Lambda and copied the ARN into the CloudFront Distribution settings.

'use strict';

exports.handler = (event, context, callback) => {
  const response = event.Records[0].cf.response;

  if (response.status === '301' || response.status === '302') {
    if (
      response.headers['location'] &&
      response.headers['location'][0].value.indexOf('USERNAME.github.io') > -1
    ) {
      response.headers['location'][0].value = response.headers['location'][0].value.replace(
        'USERNAME.github.io',
        'custom.domain'
      );
    }
  }

  callback(null, response);
};

Origin Response Lambda

Once this was complete we setup an ALIAS record for the custom domain within Route 53 and all GitHub Pages traffic could now be accessed from the custom domain over HTTPS.