By default, Hugo uses pretty URLs: your page addresses look like
https://ronniegane.kiwi/blog/
rather than /blog/index.html
or /blog.html
.
The directory structure Hugo creates for this has each page exisiting as an index.html
file inside of a directory, and then using the directory name as the page address.
To make these work with S3 we need to set up a Lambda function to rewrite the addresses to map to the actual files in s3:
/2018/11/16/pretty/
-> /2018/11/16/pretty/index.html
We also need to account for people potentially leaving off the trailing slash when they type an address, so that /2018/11/16/pretty
will still work. Originally my Lambda function didn’t do that, which led to some bugs where I forgot to add a slash to my own links. Hugo’s relURL function doesn’t add the closing slash for you.
Lastly, we don’t want to rewrite links to resources like CSS or images. So we can’t just add /index.html
to all requests, otherwise we will try to fetch /css/main.css/index.html
The Lambda function I use is mostly cribbed from this Amazon article: Implementing Default Directory Indexes in Amazon S3-backed Amazon CloudFront Origins Using Lambda@Edge
'use strict';
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda@Edge
var request = event.Records[0].cf.request;
// Extract the URI from the request
var olduri = request.uri;
// Append /index.html to any request (excluding <file>.<ext> pattern)
var newuri = olduri.replace(/(\/[^.\/]+)\/?$/, '$1\/index.html');
// Log the URI as received by CloudFront and the new URI to be used to fetch from origin
console.log("Old URI: " + olduri);
console.log("New URI: " + newuri);
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
// Return to CloudFront
return callback(null, request);
};
With this in place, we successfully redirect to the index.html
files for each page. https://ronniegane.kiwi/blog and https://ronniegane.kiwi/blog/ both work, while https://ronniegane.kiwi/css/main.css avoids redirection.