Skip to content

Commit e0cf328

Browse files
authored
fix skip trailing slash redirect (#323)
1 parent 373912c commit e0cf328

File tree

4 files changed

+49
-36
lines changed

4 files changed

+49
-36
lines changed

examples/app-pages-router/next.config.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ const nextConfig = {
88
experimental: {
99
serverActions: true,
1010
},
11+
trailingSlash: true,
12+
skipTrailingSlashRedirect: true,
1113
};
1214

1315
module.exports = nextConfig;

packages/open-next/src/adapters/routing/matcher.ts

Lines changed: 38 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -168,42 +168,44 @@ export function handleRedirects(
168168
event: InternalEvent,
169169
redirects: RedirectDefinition[],
170170
): InternalResult | undefined {
171-
if (
172-
NextConfig.trailingSlash &&
173-
!event.headers["x-nextjs-data"] &&
174-
!event.rawPath.endsWith("/") &&
175-
!event.rawPath.match(/[\w-]+\.[\w]+$/g)
176-
) {
177-
const headersLocation = event.url.split("?");
178-
return {
179-
type: event.type,
180-
statusCode: 308,
181-
headers: {
182-
Location: `${headersLocation[0]}/${
183-
headersLocation[1] ? `?${headersLocation[1]}` : ""
184-
}`,
185-
},
186-
body: "",
187-
isBase64Encoded: false,
188-
};
189-
// eslint-disable-next-line sonarjs/elseif-without-else
190-
} else if (
191-
!NextConfig.trailingSlash &&
192-
event.rawPath.endsWith("/") &&
193-
event.rawPath !== "/"
194-
) {
195-
const headersLocation = event.url.split("?");
196-
return {
197-
type: event.type,
198-
statusCode: 308,
199-
headers: {
200-
Location: `${headersLocation[0].replace(/\/$/, "")}${
201-
headersLocation[1] ? `?${headersLocation[1]}` : ""
202-
}`,
203-
},
204-
body: "",
205-
isBase64Encoded: false,
206-
};
171+
if (!NextConfig.skipTrailingSlashRedirect) {
172+
if (
173+
NextConfig.trailingSlash &&
174+
!event.headers["x-nextjs-data"] &&
175+
!event.rawPath.endsWith("/") &&
176+
!event.rawPath.match(/[\w-]+\.[\w]+$/g)
177+
) {
178+
const headersLocation = event.url.split("?");
179+
return {
180+
type: event.type,
181+
statusCode: 308,
182+
headers: {
183+
Location: `${headersLocation[0]}/${
184+
headersLocation[1] ? `?${headersLocation[1]}` : ""
185+
}`,
186+
},
187+
body: "",
188+
isBase64Encoded: false,
189+
};
190+
// eslint-disable-next-line sonarjs/elseif-without-else
191+
} else if (
192+
!NextConfig.trailingSlash &&
193+
event.rawPath.endsWith("/") &&
194+
event.rawPath !== "/"
195+
) {
196+
const headersLocation = event.url.split("?");
197+
return {
198+
type: event.type,
199+
statusCode: 308,
200+
headers: {
201+
Location: `${headersLocation[0].replace(/\/$/, "")}${
202+
headersLocation[1] ? `?${headersLocation[1]}` : ""
203+
}`,
204+
},
205+
body: "",
206+
isBase64Encoded: false,
207+
};
208+
}
207209
}
208210
const { internalEvent, __rewrite } = handleRewrites(
209211
event,

packages/open-next/src/adapters/types/next-types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ export type Header = {
6363
export interface NextConfig {
6464
basePath?: string;
6565
trailingSlash?: string;
66+
skipTrailingSlashRedirect?: boolean;
6667
i18n?: {
6768
locales: string[];
6869
};
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect, test } from "@playwright/test";
2+
3+
test("skipTrailingSlashRedirect redirect", async ({ page }) => {
4+
const response = await page.goto("/ssr");
5+
6+
expect(response?.request().redirectedFrom()).toBeNull();
7+
expect(response?.request().url()).toMatch(/\/ssr$/);
8+
});

0 commit comments

Comments
 (0)