The purpose of this guide is to walk you through the step-by-step process of creating 301 redirects for files hosted in Amazon S3 using CloudFront functions.
For more information on the differences between CloudFront functions and Lambda@Edge Functions, please visit this link.
When dealing with multiple folders within the bucket, it’s essential to specify the files and folders for which you intend to implement redirects. Otherwise, the code will be applied to every file request. Additionally, ensure that the asset exists in the new route.
Create the function #
- Go to your AWS dashboard and search for CloudFront.
- Once there, on the left sidebar click on
Functions
->Create Function
. After giving a name and description to your function, you will be redirected to the editor code with a basic code sample.
This base code will stop serving the final asset and will instead return an OK screen.
Handling Requests #
The function receives an event, and within this event lies the request data. The provided code offers 3 samples on how to handle the requests:
- If the request URI contains
/assets/old-slug/
redirect to/assets/new-slug/
- If the request URI contains one of the URLs provided in a variable, redirect to another asset.
- If the request URI ends with a known string, redirect by replacing the ending of the asset’s name.
Redirecting an asset is not limited to these 3 samples, you have the flexibility to redirect to alternative buckets or domains, modify asset header responses, implement cache key normalization, handle HTTP to HTTPS requests, and explore various other customization options. Given that the code is written in pure JavaScript, you have the freedom to use innovative approaches to achieve your desired solutions.
function handler(event) {
// NOTE: This example function is for a viewer request event trigger.
// Choose viewer request for event trigger when you associate this function with a distribution.
var domain = 'https://myS3bucketDomain.com';
var currentFolderUri = '/assets/old-slug/';
var newFolderUri = '/assets/new-slug/';
var request = event.request;
var newUri = request.uri;
var brokenUrls = [
{
"/assets/folder1/yourFilename1.pdf":"/assets/new-slug/yourFilename1.pdf",
"/assets/folder1/yourFilename2.pdf":"/assets/new-slug/yourFilename2.pdf",
"/assets/folder1/yourFilename3.pdf":"/assets/new-slug/yourFilename3.pdf",
"/assets/folder1/yourFilename4.pdf":"/assets/new-slug/yourFilename4.pdf",
"/assets/temp-folder/yourFilenameA.pdf":"/assets/new-slug/yourFilenameA.pdf",
"/assets/temp-folder/yourFilenameB.pdf":"/assets/new-slug/yourFilenameB.pdf",
"/assets/temp-folder/yourFilenameC.pdf":"/assets/new-slug/yourFilenameC.pdf",
"/assets/temp-folder/yourFilenameD.pdf":"/assets/new-slug/yourFilenameD.pdf"
}
];
if(request.uri.includes(currentFolderUri)){
newUri = request.uri.replace(currentFolderUri, newFolderUri)
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
location: {
value: domain + newUri,
},
},
};
}
//fully change a filename
if (brokenUrls[0][request.uri] !== undefined) {
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
location: {
value: domain + brokenUrls[0][request.uri],
},
},
};
}
else if (request.uri.endsWith('-categoryA.pdf')) {
newUri = request.uri.replace('-categoryA.pdf', '-categoryB.pdf')
return {
statusCode: 301,
statusDescription: 'Moved Permanently',
headers: {
location: {
value: domain + newUri,
},
},
};
}
return request;
}
From lines 23 to 34, it redirects any request that contains a URI with /assets/old-slug/
to /assets/new-slug/
.
From lines 37 to 47, it redirects any request that matches any URI in the variable brokenUrls
to its new URL.
From lines 48 to 59, it redirects any request that ends with -categoryA.pdf
to a new URL for it, ending with -categoryB.pdf
.
Lastly, line 61 return request;
will allow all the other requests to keep going with their normal route.
How to test your code. #
Since the language is Javascript, you can use console.log()
to print and debug any variable.
For example: consoel.log(request);
Once you have your code in place, click Save Changes
and go to the tab Test
. (If you don’t hit Save Changes
, you won’t see the changes on the Test
tab). Write one of your possible cases on the field URL Path
, for example /assets/old-slug/testing.pdf
, and click Test Function
.
Beneath the form, you’ll find a section displaying the Execution result
. If your code includes a console.log()
statement, the corresponding output will be visible in the Execution logs
(I included the request).
And under Output
you will see the response of your function. For this case, redirect from
myS3bucketDomain.com/assets/old-slug/testing.pdf
to
myS3bucketDomain.com/assets/new-slug/testing.pdf
Publishing the Function #
Once you are confident with your code and after testing different possible requests, it’s time to publish your function. Go to the tab Publish
and click Publish function
.
Although, the function is now available to be used, it has not been assigned to a distribution. To associate it with a distribution, navigate to the Publish
tab, click on Add Association
and choose your CloudFront distribution (you may need to open a new tab to copy the distribution ID). Select the appropriate Event type (in this case, “Viewer Request”) and configure the Cache behavior.
After you click on Add Association
, head to your distribution and click on the tab Invalidations
. Create a new invalidation containing the URI of your old files, this will ensure the cache for those files is reset.
Why did we end up using Lambda@Edge? #
This client had about 2000 unique redirects to be validated. These URLs would have to be defined on the variable brokenUrls
of our code, due to CloudFront Functions size limitations, this variable made the file too big for the available scope.
If you have any questions, feel free to contact me.