I have an application that uses NextJS as a wrapper, and I make use of NextJS's dynamic routing feature. I had a problem when deploying it to CloudFront due to dns.com/path/page
not being rendered, instead CloudFront expected it to be dns.com/path/page.html
. I worked it around by applying this lambda-edge-nice-url solution. It works properly now. However, there's still one issue left: NextJS's dynamic routes. dsn.com/path/subpath/123
should work, since 123 is a dynamic parameter. However, that does no work. In only returns the page when I access dns.com/path/subpath/[id]
, which of course is not correct, since [id] is not a parameter I want to load.
The strangest thing is: if I try to access the URL as I stated above directly, it fails. However, inside the application I have buttons and links that redirect the user, and that works properly.
Navigating from inside the application (button with router.push
inside its callback):
Trying to access the url directly:
Can anyone help me to properly route the requests?
After trying a lot of different code, I finally came up with a Lambda edge expression that fixed two issues in one:
.html
at the end of the URLThe code below basically takes care of dynamic routes first. It uses a regex expression to understand the current URL and redirect the request to the proper [id].html
file. After that, if the none of the regex are matched, and if the URL does not contain .html
extension, it adds the extension and retrieves the correct file.
const config = {
suffix: '.html',
appendToDirs: 'index.html',
removeTrailingSlash: false,
};
const regexSuffixless = /\/[^/.]+$/; // e.g. "/some/page" but not "/", "/some/" or "/some.jpg"
const regexTrailingSlash = /.+\/$/; // e.g. "/some/" or "/some/page/" but not root "/"
const dynamicRouteRegex = /\/subpath\/\b[0-9a-f]{8}\b-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-\b[0-9a-f]{12}\b/; // e.g /urs/some-uuid; // e.g. '/subpath/uuid'
exports.handler = function handler(event, context, callback) {
const { request } = event.Records[0].cf;
const { uri } = request;
const { suffix, appendToDirs, removeTrailingSlash } = config;
//Checks for dynamic route and retrieves the proper [id].html file
if (uri.match(dynamicRouteRegex)) {
request.uri = "/subpath/[id].html";
callback(null, request);
return;
}
// Append ".html" to origin request
if (suffix && uri.match(regexSuffixless)) {
request.uri = uri + suffix;
callback(null, request);
return;
}
// Append "index.html" to origin request
if (appendToDirs && uri.match(regexTrailingSlash)) {
request.uri = uri + appendToDirs;
callback(null, request);
return;
}
// Redirect (301) non-root requests ending in "/" to URI without trailing slash
if (removeTrailingSlash && uri.match(/.+\/$/)) {
const response = {
// body: '',
// bodyEncoding: 'text',
headers: {
'location': [{
key: 'Location',
value: uri.slice(0, -1)
}]
},
status: '301',
statusDescription: 'Moved Permanently'
};
callback(null, response);
return;
}
// If nothing matches, return request unchanged
callback(null, request);
};
Many thanks to @LongZheng for his answer. For some reason his code did not work for me, but it might for some, so check his answer out. Also, big shoutout to Manc, the creator of this lambda-edge-nice-urls repo. My code is basically a mix of both.