From 475971604b4771ca26dde9207f6b26dda96bf272 Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Fri, 3 Apr 2026 09:34:56 +0200 Subject: [PATCH 1/2] Trim trailing slash on CloudFront via 301 redirect --- scripts/handle_redirects.js | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/scripts/handle_redirects.js b/scripts/handle_redirects.js index 2a6280c517..55cc7633fa 100644 --- a/scripts/handle_redirects.js +++ b/scripts/handle_redirects.js @@ -9,10 +9,6 @@ const staticAssetRegex = const versionRegex = /^\/(\d+\.\d+)(\/.*)?/; -function stripTrailingSlash(uri) { - return uri.endsWith("/") ? uri.slice(0, -1) : uri; -} - function compareVersions(v1, v2) { const parts1 = v1.split("."); const parts2 = v2.split("."); @@ -45,21 +41,29 @@ function compareVersions(v1, v2) { async function handler(event) { const request = event.request; const uri = request.uri; - const normalizedUri = stripTrailingSlash(uri); - if (staticAssetRegex.test(uri)) { return request; } - request.uri = normalizedUri + "/index.html"; + if (uri !== "/" && uri.endsWith("/")) { + return { + statusCode: 301, + statusDescription: "Moved Permanently", + headers: { + location: { value: uri.slice(0, -1) }, + }, + }; + } + + request.uri = uri + "/index.html"; - if (normalizedUri.startsWith("/templates")) { + if (uri.startsWith("/templates")) { return request; } - if (normalizedUri.startsWith("/guides") || normalizedUri.startsWith("/cloud")) { + if (uri.startsWith("/guides") || uri.startsWith("/cloud")) { try { - const redirectData = await kvsHandle.get(normalizedUri); + const redirectData = await kvsHandle.get(uri); const redirectJsonValue = JSON.parse(redirectData); if (redirectJsonValue.targetUrl) { return { @@ -76,17 +80,17 @@ async function handler(event) { return request; } - const versionMatch = normalizedUri.match(versionRegex); + const versionMatch = uri.match(versionRegex); let version, versionlessUri, targetUri; let redirectRequired = false; if (versionMatch) { version = versionMatch[1]; - versionlessUri = versionMatch[2] || "/"; + versionlessUri = versionMatch[2] || ""; } else { version = defaultVersion; - versionlessUri = normalizedUri; + versionlessUri = uri === "/" ? "" : uri; redirectRequired = true; } From 3297fbe6ba23778432805cfba43dc6e67060a5ad Mon Sep 17 00:00:00 2001 From: Gracjan Sadowicz Date: Fri, 3 Apr 2026 10:13:38 +0200 Subject: [PATCH 2/2] Avoid doubled redirects when there's entry in redirects.json --- scripts/handle_redirects.js | 63 ++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 32 deletions(-) diff --git a/scripts/handle_redirects.js b/scripts/handle_redirects.js index 55cc7633fa..34ef4323b5 100644 --- a/scripts/handle_redirects.js +++ b/scripts/handle_redirects.js @@ -38,59 +38,63 @@ function compareVersions(v1, v2) { } } +function redirect(targetUrl) { + return { + statusCode: 301, + statusDescription: "Moved Permanently", + headers: { + location: { value: targetUrl }, + }, + }; +} + async function handler(event) { const request = event.request; const uri = request.uri; + if (staticAssetRegex.test(uri)) { return request; } - if (uri !== "/" && uri.endsWith("/")) { - return { - statusCode: 301, - statusDescription: "Moved Permanently", - headers: { - location: { value: uri.slice(0, -1) }, - }, - }; - } + const hasTrailingSlash = uri !== "/" && uri.endsWith("/"); + const normalizedUri = hasTrailingSlash ? uri.slice(0, -1) : uri; - request.uri = uri + "/index.html"; - - if (uri.startsWith("/templates")) { + if (normalizedUri.startsWith("/templates")) { + if (hasTrailingSlash) { + return redirect(normalizedUri); + } + request.uri = uri + "/index.html"; return request; } - if (uri.startsWith("/guides") || uri.startsWith("/cloud")) { + if (normalizedUri.startsWith("/guides") || normalizedUri.startsWith("/cloud")) { try { - const redirectData = await kvsHandle.get(uri); + const redirectData = await kvsHandle.get(normalizedUri); const redirectJsonValue = JSON.parse(redirectData); if (redirectJsonValue.targetUrl) { - return { - statusCode: 301, - statusDescription: "Moved Permanently", - headers: { - location: { value: redirectJsonValue.targetUrl }, - }, - }; + return redirect(redirectJsonValue.targetUrl); } } catch (_) { - // No redirect rule found — pass through as-is + // No redirect rule found + } + if (hasTrailingSlash) { + return redirect(normalizedUri); } + request.uri = uri + "/index.html"; return request; } - const versionMatch = uri.match(versionRegex); + const versionMatch = normalizedUri.match(versionRegex); let version, versionlessUri, targetUri; - let redirectRequired = false; + let redirectRequired = hasTrailingSlash; if (versionMatch) { version = versionMatch[1]; versionlessUri = versionMatch[2] || ""; } else { version = defaultVersion; - versionlessUri = uri === "/" ? "" : uri; + versionlessUri = normalizedUri === "/" ? "" : normalizedUri; redirectRequired = true; } @@ -113,14 +117,9 @@ async function handler(event) { } if (redirectRequired) { - return { - statusCode: 301, - statusDescription: "Moved Permanently", - headers: { - location: { value: targetUri }, - }, - }; + return redirect(targetUri); } + request.uri = normalizedUri + "/index.html"; return request; }