diff --git a/.changeset/proud-dogs-punch.md b/.changeset/proud-dogs-punch.md
new file mode 100644
index 000000000..ad9876617
--- /dev/null
+++ b/.changeset/proud-dogs-punch.md
@@ -0,0 +1,5 @@
+---
+"@opennextjs/aws": patch
+---
+
+fix: Ensure Location header is properly encoded in redirects happening from next config
diff --git a/examples/app-router/app/config-redirect/dest/page.tsx b/examples/app-router/app/config-redirect/dest/page.tsx
new file mode 100644
index 000000000..e9ae5d056
--- /dev/null
+++ b/examples/app-router/app/config-redirect/dest/page.tsx
@@ -0,0 +1,13 @@
+export default async function Page({
+ searchParams,
+}: {
+ searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
+}) {
+ const q = (await searchParams).q;
+
+ return (
+ <>
+
q: {q}
+ >
+ );
+}
diff --git a/examples/app-router/app/config-redirect/page.tsx b/examples/app-router/app/config-redirect/page.tsx
index 6e51d3d56..c66280060 100644
--- a/examples/app-router/app/config-redirect/page.tsx
+++ b/examples/app-router/app/config-redirect/page.tsx
@@ -3,6 +3,18 @@ export default function RedirectDestination() {
);
}
diff --git a/examples/app-router/next.config.ts b/examples/app-router/next.config.ts
index d1356ef9f..77bd5abf0 100644
--- a/examples/app-router/next.config.ts
+++ b/examples/app-router/next.config.ts
@@ -6,13 +6,6 @@ const nextConfig: NextConfig = {
transpilePackages: ["@example/shared"],
output: "standalone",
// outputFileTracingRoot: "../sst",
- eslint: {
- ignoreDuringBuilds: true,
- },
- //TODO: remove this when i'll figure out why it fails locally
- typescript: {
- ignoreBuildErrors: true,
- },
images: {
remotePatterns: [
{
@@ -60,6 +53,11 @@ const nextConfig: NextConfig = {
basePath: false,
locale: false,
},
+ {
+ source: "/next-config-redirect-encoding",
+ destination: "/config-redirect/dest",
+ permanent: false,
+ },
];
},
async headers() {
diff --git a/packages/open-next/src/core/routingHandler.ts b/packages/open-next/src/core/routingHandler.ts
index ed3f926e1..834bf4f58 100644
--- a/packages/open-next/src/core/routingHandler.ts
+++ b/packages/open-next/src/core/routingHandler.ts
@@ -96,6 +96,11 @@ export default async function routingHandler(
const redirect = handleRedirects(internalEvent, RoutesManifest.redirects);
if (redirect) {
+ // We need to encode the value in the Location header to make sure it is valid according to RFC
+ // https://stackoverflow.com/a/7654605/16587222
+ redirect.headers.Location = new URL(
+ redirect.headers.Location as string,
+ ).href;
debug("redirect", redirect);
return redirect;
}
diff --git a/packages/tests-e2e/tests/appRouter/config.redirect.test.ts b/packages/tests-e2e/tests/appRouter/config.redirect.test.ts
index d328d04e2..218e83279 100644
--- a/packages/tests-e2e/tests/appRouter/config.redirect.test.ts
+++ b/packages/tests-e2e/tests/appRouter/config.redirect.test.ts
@@ -72,4 +72,46 @@ test.describe("Next Config Redirect", () => {
});
await expect(el).toBeVisible();
});
+ test("Should properly encode the Location header for redirects with query params", async ({
+ page,
+ baseURL,
+ }) => {
+ await page.goto("/config-redirect");
+ const responsePromise = page.waitForResponse((response) => {
+ return response.status() === 307;
+ });
+ page.getByTestId("redirect-link").click();
+ const res = await responsePromise;
+ await page.waitForURL("/config-redirect/dest?q=äöå€");
+
+ const locationHeader = res.headers().location;
+ expect(locationHeader).toBe(
+ `${baseURL}/config-redirect/dest?q=%C3%A4%C3%B6%C3%A5%E2%82%AC`,
+ );
+ expect(res.status()).toBe(307);
+
+ const searchParams = page.getByTestId("searchParams");
+ await expect(searchParams).toHaveText("q: äöå€");
+ });
+ test("Should respect already encoded query params", async ({
+ page,
+ baseURL,
+ }) => {
+ await page.goto("/config-redirect");
+ const responsePromise = page.waitForResponse((response) => {
+ return response.status() === 307;
+ });
+ page.getByTestId("redirect-link-already-encoded").click();
+ const res = await responsePromise;
+ await page.waitForURL("/config-redirect/dest?q=äöå€");
+
+ const locationHeader = res.headers().location;
+ expect(locationHeader).toBe(
+ `${baseURL}/config-redirect/dest?q=%C3%A4%C3%B6%C3%A5%E2%82%AC`,
+ );
+ expect(res.status()).toBe(307);
+
+ const searchParams = page.getByTestId("searchParams");
+ await expect(searchParams).toHaveText("q: äöå€");
+ });
});