File tree Expand file tree Collapse file tree 4 files changed +62
-4
lines changed
packages/open-next/src/core Expand file tree Collapse file tree 4 files changed +62
-4
lines changed Original file line number Diff line number Diff line change 1+ ---
2+ " @opennextjs/aws " : patch
3+ ---
4+
5+ fix: Normalize the Location header in redirects
6+
7+ Normalizes the Location header to either be a relative path or a full URL.
8+ If the Location header is relative to the host, it will return a relative path.
9+ If it is an absolute URL, it will return the full URL.
10+ Both cases will ensure that the Location value is properly encoded according to RFC
Original file line number Diff line number Diff line change @@ -15,6 +15,7 @@ import {
1515 convertBodyToReadableStream ,
1616 getMiddlewareMatch ,
1717 isExternal ,
18+ normalizeLocationHeader ,
1819} from "./util.js" ;
1920
2021const middlewareManifest = MiddlewareManifest ;
@@ -94,6 +95,15 @@ export async function handleMiddleware(
9495 url,
9596 body : convertBodyToReadableStream ( internalEvent . method , internalEvent . body ) ,
9697 } as unknown as Request ) ;
98+ if ( result . headers . has ( "Location" ) ) {
99+ result . headers . set (
100+ "Location" ,
101+ normalizeLocationHeader (
102+ result . headers . get ( "Location" ) as string ,
103+ internalEvent . url ,
104+ ) ,
105+ ) ;
106+ }
97107 const statusCode = result . status ;
98108
99109 /* Apply override headers from middleware
Original file line number Diff line number Diff line change @@ -437,3 +437,41 @@ export async function invalidateCDNOnRequest(
437437 ] ) ;
438438 }
439439}
440+
441+ /**
442+ * Normalizes the Location header to either be a relative path or a full URL.
443+ * If the Location header is relative to the host, it will return a relative path.
444+ * If it is an absolute URL, it will return the full URL.
445+ * Both cases will ensure that the Location value is properly encoded according to RFC
446+ *
447+ * @param location The Location header value
448+ * @param base The original request URL
449+ * @returns An encoded absolute or relative Location header value
450+ */
451+ export function normalizeLocationHeader (
452+ location : string ,
453+ base : string ,
454+ ) : string {
455+ try {
456+ const locationUrl = new URL ( location ) ;
457+ const origin = new URL ( base ) . origin ;
458+
459+ // Encode the search parameters to ensure they are valid according to RFC
460+ const encodedSearch = locationUrl . searchParams . toString ( )
461+ ? `?${ locationUrl . searchParams . toString ( ) } `
462+ : "" ;
463+ const href =
464+ locationUrl . origin +
465+ locationUrl . pathname +
466+ encodedSearch +
467+ locationUrl . hash ;
468+ // The URL is relative if the origin is the same as the base URL's origin
469+ if ( locationUrl . origin === origin ) {
470+ return href . slice ( origin . length ) ;
471+ }
472+ return href ;
473+ } catch {
474+ // If the location is not a valid URL, return it as-is
475+ return location ;
476+ }
477+ }
Original file line number Diff line number Diff line change @@ -28,7 +28,7 @@ import {
2828 dynamicRouteMatcher ,
2929 staticRouteMatcher ,
3030} from "./routing/routeMatcher" ;
31- import { constructNextUrl } from "./routing/util" ;
31+ import { constructNextUrl , normalizeLocationHeader } from "./routing/util" ;
3232
3333export const MIDDLEWARE_HEADER_PREFIX = "x-middleware-response-" ;
3434export const MIDDLEWARE_HEADER_PREFIX_LEN = MIDDLEWARE_HEADER_PREFIX . length ;
@@ -110,13 +110,13 @@ export default async function routingHandler(
110110 if ( redirect ) {
111111 // We need to encode the value in the Location header to make sure it is valid according to RFC
112112 // https://stackoverflow.com/a/7654605/16587222
113- redirect . headers . Location = new URL (
113+ redirect . headers . Location = normalizeLocationHeader (
114114 redirect . headers . Location as string ,
115- ) . href ;
115+ event . url ,
116+ ) ;
116117 debug ( "redirect" , redirect ) ;
117118 return redirect ;
118119 }
119-
120120 const middlewareEventOrResult = await handleMiddleware (
121121 eventOrResult ,
122122 // We need to pass the initial search without any decoding
You can’t perform that action at this time.
0 commit comments