@@ -9,6 +9,7 @@ import type {
99 RouteHas ,
1010} from "types/next-types" ;
1111import type { InternalEvent , InternalResult } from "types/open-next" ;
12+ import { normalizeRepeatedSlashes } from "utils/normalize-path" ;
1213import { emptyReadableStream , toReadableStream } from "utils/stream" ;
1314
1415import { debug } from "../../adapters/logger" ;
@@ -262,6 +263,29 @@ export function handleRewrites<T extends RewriteDefinition>(
262263 } ;
263264}
264265
266+ // Normalizes repeated slashes in the path e.g. hello//world -> hello/world
267+ // or backslashes to forward slashes. This prevents requests such as //domain
268+ // from invoking the middleware with `request.url === "domain"`.
269+ // See: https://github.com/vercel/next.js/blob/3ecf087f10fdfba4426daa02b459387bc9c3c54f/packages/next/src/server/base-server.ts#L1016-L1020
270+ function handleRepeatedSlashRedirect (
271+ event : InternalEvent ,
272+ ) : false | InternalResult {
273+ // Redirect `https://example.com//foo` to `https://example.com/foo`.
274+ if ( event . rawPath . match ( / ( \\ | \/ \/ ) / ) ) {
275+ return {
276+ type : event . type ,
277+ statusCode : 308 ,
278+ headers : {
279+ Location : normalizeRepeatedSlashes ( new URL ( event . url ) ) ,
280+ } ,
281+ body : emptyReadableStream ( ) ,
282+ isBase64Encoded : false ,
283+ } ;
284+ }
285+
286+ return false ;
287+ }
288+
265289function handleTrailingSlashRedirect (
266290 event : InternalEvent ,
267291) : false | InternalResult {
@@ -326,6 +350,9 @@ export function handleRedirects(
326350 event : InternalEvent ,
327351 redirects : RedirectDefinition [ ] ,
328352) : InternalResult | undefined {
353+ const repeatedSlashRedirect = handleRepeatedSlashRedirect ( event ) ;
354+ if ( repeatedSlashRedirect ) return repeatedSlashRedirect ;
355+
329356 const trailingSlashRedirect = handleTrailingSlashRedirect ( event ) ;
330357 if ( trailingSlashRedirect ) return trailingSlashRedirect ;
331358
0 commit comments