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 solely 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 the origin. Doing so allowed us to supply our own SSL certificate and a secure connection for our custom domain. Below is a high-level diagram depicting the approach we took.
We first set up a new distribution which was associated with a certificate supplied by AWS Certificate Manager.
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.
With this origin in place, we then created a default cache behaviour which enforced HTTPS traffic (redirecting HTTP traffic if found).
Handling GitHub Page Redirects
One issue we faced with routing traffic through CloudFront was that if GitHub attempted to return a redirect response to the client (for example, for missing trailing slashes), GitHub, being unaware of the custom domain, would attempt to route traffic to the *.github.io
domain.
To get around this, we took advantage of an 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);
};
Once this was complete, we set up an ALIAS
record for the custom domain within Route 53.
All GitHub Pages traffic could now be accessed from the custom domain over HTTPS.