Skip to content

Commit b8911ef

Browse files
committed
refactor to match Next behavior
1 parent 858d514 commit b8911ef

File tree

2 files changed

+19
-10
lines changed

2 files changed

+19
-10
lines changed

packages/open-next/src/core/routing/util.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import crypto from "node:crypto";
22
import type { OutgoingHttpHeaders } from "node:http";
3+
import { parse as parseQs, stringify as stringifyQs } from "node:querystring";
34
import { Readable } from "node:stream";
45

56
import { BuildId, HtmlPages, NextConfig } from "config/index.js";
@@ -442,31 +443,39 @@ export async function invalidateCDNOnRequest(
442443
* Normalizes the Location header to either be a relative path or a full URL.
443444
* If the Location header is relative to the origin, it will return a relative path.
444445
* 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+
* Redirects from Next config query parameters are encoded using `stringifyQs`
447+
* Redirects from the middleware the query parameters are not encoded.
446448
*
447449
* @param location The Location header value
448450
* @param baseUrl The base URL to use for relative paths (i.e the original request URL)
449-
* @returns An encoded absolute or relative Location header value
451+
* @param encodeQuery Optional flag to indicate if query parameters should be encoded in the Location header
452+
* @returns An absolute or relative Location header value
450453
*/
451454
export function normalizeLocationHeader(
452455
location: string,
453456
baseUrl: string,
457+
encodeQuery = false,
454458
): string {
455459
if (!URL.canParse(location)) {
456460
// If the location is not a valid URL, return it as-is
457461
return location;
458462
}
459463

460-
const locationUrl = new URL(location);
464+
const locationURL = new URL(location);
461465
const origin = new URL(baseUrl).origin;
462466

463-
// Encode the search parameters to ensure they are valid according to RFC
464-
const searchParams = locationUrl.searchParams.toString();
465-
const encodedSearch = searchParams ? `?${searchParams}` : "";
466-
const href = `${locationUrl.origin}${locationUrl.pathname}${encodedSearch}${locationUrl.hash}`;
467-
467+
// Redirects from the middleware do not encode the query parameters
468+
let search = locationURL.search;
469+
// If encodeQuery is true, we need to encode the query parameters
470+
// This is used for redirects from Next config
471+
// We could have used URLSearchParams, but that encodes it too much. stringify from querystring uses encodeURIComponent
472+
// which is what Next.js does for redirects from the config
473+
if (encodeQuery) {
474+
search = search ? `?${stringifyQs(parseQs(search.slice(1)))}` : "";
475+
}
476+
const href = `${locationURL.origin}${locationURL.pathname}${search}${locationURL.hash}`;
468477
// The URL is relative if the origin is the same as the base URL's origin
469-
if (locationUrl.origin === origin) {
478+
if (locationURL.origin === origin) {
470479
return href.slice(origin.length);
471480
}
472481
return href;

packages/open-next/src/core/routingHandler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,10 @@ export default async function routingHandler(
109109
const redirect = handleRedirects(eventOrResult, RoutesManifest.redirects);
110110
if (redirect) {
111111
// We need to encode the value in the Location header to make sure it is valid according to RFC
112-
// https://stackoverflow.com/a/7654605/16587222
113112
redirect.headers.Location = normalizeLocationHeader(
114113
redirect.headers.Location as string,
115114
event.url,
115+
true,
116116
);
117117
debug("redirect", redirect);
118118
return redirect;

0 commit comments

Comments
 (0)