Last active
March 7, 2023 20:31
-
-
Save nabilfreeman/c0dec56f149fcfb4a8e704ae672b204e to your computer and use it in GitHub Desktop.
Redirect to trailing slashes on CloudFront with AWS Lambda. (all this because S3 uses 302 redirects instead of 301)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
'use strict'; | |
const path = require('path') | |
const redirect = new_url => { | |
return { | |
status: '301', | |
statusDescription: 'Moved Permanently', | |
headers: { | |
location: [{ | |
key: 'Location', | |
value: new_url, | |
}], | |
}, | |
}; | |
}; | |
exports.handler = (event, context, callback) => { | |
//get request object | |
const { request } = event.Records[0].cf | |
//if this url has uppercase letters in it, we need to direct to the all-lowercase version. | |
const regex = /[A-Z]/g; | |
//if we get a truthy result from the regex match, we need to 301 to the lowercase url. | |
if(request.uri.match(regex)){ | |
//lowercase the url. | |
let lowercase_url = `${request.uri.toLowerCase()}/`; | |
if(request.querystring) { | |
lowercase_url += `?${request.querystring}`; | |
} | |
//debug. | |
console.log(`Rewriting ${request.uri} to ${lowercase_url}...`); | |
//create HTTP redirect... | |
return callback(null, redirect(lowercase_url)); | |
} | |
//we need to determine if this request has an extension. | |
const extension = path.extname(request.uri); | |
//path.extname returns an empty string when there's no extension. | |
//if there is an extension on this request, continue without doing anything! | |
if(extension && extension.length > 0){ | |
console.log(`Skipping ${request.uri} because it has a file extension.`); | |
return callback(null, request); | |
} | |
//there is no extension, so that means we want to add a trailing slash to the url. | |
//let's check if the last character is a slash. | |
const last_character = request.uri.slice(-1); | |
//if there is already a trailing slash, return. | |
if(last_character === "/"){ | |
console.log(`Skipping ${request.uri} because it already has a trailing slash.`); | |
return callback(null, request); | |
} | |
//add a trailing slash. | |
let trailing_slash_url = `${request.uri}/`; | |
if(request.querystring) { | |
trailing_slash_url += `?${request.querystring}`; | |
} | |
//debug. | |
console.log(`Rewriting ${request.uri} to ${trailing_slash_url}...`); | |
//create HTTP redirect... | |
return callback(null, redirect(trailing_slash_url)); | |
}; |
Thanks @hacklschorsch - great spot
Actually, I couldn't get this working because CloudFront does not expose the original host in a consistent way - so if you are using a custom domain against your distribution, you will get undefined in your 301. So I've put back.
To future readers: If you are just using a x.cloudfront.net
domain, check out the revisions and pick out a previous one with the above fix in it.
Also, I added some code to ensure that paths get lowercased.
Interested in building a script like yours.
What is the consistency issue you are seeing?
Have you tried with CloudFront functions?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I believe that will give you an open redirect vulnerability if someone does, for example:
http://your-host//example.com/index
Changing
to
or so should mitigate that IIANM