diff --git a/packages/react-router/.changes/patch.normalize-url-control-characters.md b/packages/react-router/.changes/patch.normalize-url-control-characters.md
deleted file mode 100644
index 1fcb82fe34..0000000000
--- a/packages/react-router/.changes/patch.normalize-url-control-characters.md
+++ /dev/null
@@ -1 +0,0 @@
-Improve handling of special characters in navigation paths
diff --git a/packages/react-router/__tests__/dom/link-href-test.tsx b/packages/react-router/__tests__/dom/link-href-test.tsx
index 589ec13300..861de56e30 100644
--- a/packages/react-router/__tests__/dom/link-href-test.tsx
+++ b/packages/react-router/__tests__/dom/link-href-test.tsx
@@ -129,26 +129,6 @@ describe(" href", () => {
expect(renderer.root.findByType("a").props.href).toEqual("//remix.run");
});
- test("normalizes special characters in relative values", () => {
- let renderer: TestRenderer.ReactTestRenderer;
- TestRenderer.act(() => {
- renderer = TestRenderer.create(
-
-
-
- }
- />
-
-
- ,
- );
- });
-
- expect(renderer.root.findByType("a").props.href).toEqual("/nested/path");
- });
-
test(' is treated as external link', () => {
let renderer: TestRenderer.ReactTestRenderer;
TestRenderer.act(() => {
diff --git a/packages/react-router/__tests__/router/browser-test.ts b/packages/react-router/__tests__/router/browser-test.ts
index 1febdb99ff..d54373171d 100644
--- a/packages/react-router/__tests__/router/browser-test.ts
+++ b/packages/react-router/__tests__/router/browser-test.ts
@@ -47,12 +47,6 @@ describe("a browser history", () => {
expect(href).toEqual("/the/path?the=query#the-hash");
});
- it("normalizes special characters in relative hrefs", () => {
- for (let char of ["\t", "\n", "\r"]) {
- expect(history.createHref(`/${char}/nested/path`)).toBe("/nested/path");
- }
- });
-
it("does not encode the generated path", () => {
const encodedHref = history.createHref({
pathname: "/%23abc",
diff --git a/packages/react-router/__tests__/router/redirects-test.ts b/packages/react-router/__tests__/router/redirects-test.ts
index 8060380c7d..86d5f99d10 100644
--- a/packages/react-router/__tests__/router/redirects-test.ts
+++ b/packages/react-router/__tests__/router/redirects-test.ts
@@ -1,6 +1,6 @@
import { createMemoryHistory } from "../../lib/router/history";
import { IDLE_NAVIGATION, createRouter } from "../../lib/router/router";
-import { redirect, replace } from "../../lib/router/utils";
+import { replace } from "../../lib/router/utils";
import type { TestRouteObject } from "./utils/data-router-setup";
import { cleanup, setup } from "./utils/data-router-setup";
import { createFormData, tick } from "./utils/utils";
@@ -482,25 +482,6 @@ describe("redirects", () => {
}
});
- it("normalizes special characters in redirects", async () => {
- let router = createRouter({
- history: createMemoryHistory(),
- routes: [
- { path: "/" },
- { path: "/start", loader: () => redirect("/\t/parent") },
- { path: "/parent" },
- ],
- });
- router.initialize();
- await tick();
-
- await router.navigate("/start");
- expect(router.state.location).toMatchObject({
- pathname: "/parent",
- });
- router.dispose();
- });
-
it("properly handles same-origin absolute URLs when using a basename", async () => {
let t = setup({ routes: REDIRECT_ROUTES, basename: "/base" });
diff --git a/packages/react-router/lib/dom/lib.tsx b/packages/react-router/lib/dom/lib.tsx
index 65118ae3c5..32d5d93de6 100644
--- a/packages/react-router/lib/dom/lib.tsx
+++ b/packages/react-router/lib/dom/lib.tsx
@@ -38,13 +38,13 @@ import {
defaultMapRouteProperties,
ErrorResponseImpl,
SUPPORTED_ERROR_TYPES,
- isAbsoluteUrl,
joinPaths,
matchPath,
parseToInfo,
resolveTo,
stripBasename,
} from "../router/utils";
+import { ABSOLUTE_URL_REGEX } from "../router/url";
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import type * as _ from "./global";
@@ -1334,7 +1334,7 @@ export const Link = React.forwardRef(
) {
let { basename, navigator, useTransitions } =
React.useContext(NavigationContext);
- let isAbsolute = typeof to === "string" && isAbsoluteUrl(to);
+ let isAbsolute = typeof to === "string" && ABSOLUTE_URL_REGEX.test(to);
let parsed = parseToInfo(to, basename);
to = parsed.to;
@@ -1944,7 +1944,8 @@ export const Form = React.forwardRef(
let formAction = useFormAction(action, { relative });
let formMethod: HTMLFormMethod =
method.toLowerCase() === "get" ? "get" : "post";
- let isAbsolute = typeof action === "string" && isAbsoluteUrl(action);
+ let isAbsolute =
+ typeof action === "string" && ABSOLUTE_URL_REGEX.test(action);
let submitHandler: React.SubmitEventHandler = (event) => {
onSubmit && onSubmit(event);
diff --git a/packages/react-router/lib/dom/server.tsx b/packages/react-router/lib/dom/server.tsx
index 1fd29f0269..0713618bbe 100644
--- a/packages/react-router/lib/dom/server.tsx
+++ b/packages/react-router/lib/dom/server.tsx
@@ -24,7 +24,7 @@ import {
convertRoutesToDataRoutes,
isRouteErrorResponse,
} from "../router/utils";
-import { ABSOLUTE_URL_REGEX, normalizeRelativeUrl } from "../router/url";
+import { ABSOLUTE_URL_REGEX } from "../router/url";
import { DataRoutes, Router } from "../components";
import {
DataRouterContext,
@@ -457,7 +457,6 @@ function createHref(to: To) {
function encodeLocation(to: To): Path {
let href = typeof to === "string" ? to : createPath(to);
- href = normalizeRelativeUrl(href);
// Treating this as a full URL will strip any trailing spaces so we need to
// pre-encode them since they might be part of a matching splat param from
// an ancestor route
diff --git a/packages/react-router/lib/router/history.ts b/packages/react-router/lib/router/history.ts
index b4cc9894a0..08148d6a0e 100644
--- a/packages/react-router/lib/router/history.ts
+++ b/packages/react-router/lib/router/history.ts
@@ -1,4 +1,4 @@
-import { normalizeRelativeUrl, PROTOCOL_RELATIVE_URL_REGEX } from "./url";
+import { PROTOCOL_RELATIVE_URL_REGEX } from "./url";
////////////////////////////////////////////////////////////////////////////////
//#region Types and Constants
@@ -407,7 +407,7 @@ export function createBrowserHistory(
}
function createBrowserHref(window: Window, to: To) {
- return normalizeRelativeUrl(typeof to === "string" ? to : createPath(to));
+ return typeof to === "string" ? to : createPath(to);
}
return getUrlBasedHistory(
@@ -798,7 +798,6 @@ export function createBrowserURLImpl(
invariant(base, "No window.location.(origin|href) available to create URL");
let href = typeof to === "string" ? to : createPath(to);
- href = normalizeRelativeUrl(href);
// Treating this as a full URL will strip any trailing spaces so we need to
// pre-encode them since they might be part of a matching splat param from
diff --git a/packages/react-router/lib/router/router.ts b/packages/react-router/lib/router/router.ts
index 5b320d0b49..db266eab0e 100644
--- a/packages/react-router/lib/router/router.ts
+++ b/packages/react-router/lib/router/router.ts
@@ -77,7 +77,6 @@ import {
} from "./utils";
import {
normalizeProtocolRelativeUrl,
- normalizeRelativeUrl,
PROTOCOL_RELATIVE_URL_REGEX,
} from "./url";
@@ -6909,8 +6908,6 @@ function normalizeRedirectLocation(
basename: string,
historyInstance: History,
): string {
- location = normalizeRelativeUrl(location);
-
if (isAbsoluteUrl(location)) {
// Strip off the protocol+origin for same-origin + same-basename absolute redirects
let normalizedLocation = location;
diff --git a/packages/react-router/lib/router/url.ts b/packages/react-router/lib/router/url.ts
index 7c2e61cf9c..078b92a292 100644
--- a/packages/react-router/lib/router/url.ts
+++ b/packages/react-router/lib/router/url.ts
@@ -1,25 +1,6 @@
export const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i;
export const PROTOCOL_RELATIVE_URL_REGEX = /^[\\/]{2}/;
-// Normalize characters ignored by the URL parser before determining whether a
-// URL is relative or absolute.
-export function normalizeRelativeUrl(url: string): string {
- if (ABSOLUTE_URL_REGEX.test(url)) {
- return url;
- }
-
- let normalized = url.replace(/[\t\n\r]/g, "");
- if (!ABSOLUTE_URL_REGEX.test(normalized)) {
- return normalized;
- }
-
- if (PROTOCOL_RELATIVE_URL_REGEX.test(normalized)) {
- return normalized.replace(/^[\\/]+/, "/");
- }
-
- return normalized.replace(/^([a-z][a-z0-9+.-]*):/i, "$1%3A");
-}
-
export function normalizeProtocolRelativeUrl(url: string, protocol: string) {
return protocol + url.replace(/\\/g, "/");
}
diff --git a/packages/react-router/lib/router/utils.ts b/packages/react-router/lib/router/utils.ts
index 338394fe0d..fdc6c62c9b 100644
--- a/packages/react-router/lib/router/utils.ts
+++ b/packages/react-router/lib/router/utils.ts
@@ -5,7 +5,6 @@ import { invariant, parsePath, warning } from "./history";
import {
ABSOLUTE_URL_REGEX,
normalizeProtocolRelativeUrl,
- normalizeRelativeUrl,
PROTOCOL_RELATIVE_URL_REGEX,
} from "./url";
@@ -1854,8 +1853,7 @@ export function prependBasename({
return pathname === "/" ? basename : joinPaths([basename, pathname]);
}
-export const isAbsoluteUrl = (url: string) =>
- ABSOLUTE_URL_REGEX.test(normalizeRelativeUrl(url));
+export const isAbsoluteUrl = (url: string) => ABSOLUTE_URL_REGEX.test(url);
/**
* Returns a resolved {@link Path} object relative to the given pathname.
@@ -1876,7 +1874,6 @@ export function resolvePath(to: To, fromPathname = "/"): Path {
let pathname: string;
if (toPathname) {
- toPathname = normalizeRelativeUrl(toPathname);
toPathname = removeDoubleSlashes(toPathname);
if (toPathname.startsWith("/")) {
pathname = resolvePathname(toPathname.substring(1), "/");
@@ -2410,9 +2407,7 @@ export function parseToInfo(
_to: T,
basename: string,
): ParsedLocationInfo {
- let to = (
- typeof _to === "string" ? normalizeRelativeUrl(_to) : _to
- ) as string;
+ let to = _to as string;
if (typeof to !== "string" || !ABSOLUTE_URL_REGEX.test(to)) {
return {
absoluteURL: undefined,
diff --git a/packages/react-router/lib/rsc/browser.tsx b/packages/react-router/lib/rsc/browser.tsx
index 0a38484a5e..0daef296f5 100644
--- a/packages/react-router/lib/rsc/browser.tsx
+++ b/packages/react-router/lib/rsc/browser.tsx
@@ -25,10 +25,7 @@ import type {
RouterContextProvider,
} from "../router/utils";
import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils";
-import {
- normalizeRelativeUrl,
- PROTOCOL_RELATIVE_URL_REGEX,
-} from "../router/url";
+import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url";
import type {
DecodedSingleFetchResults,
FetchAndDecodeFunction,
@@ -1134,8 +1131,6 @@ function isExternalLocation(location: string) {
}
function normalizeRedirectLocation(location: string): string {
- location = normalizeRelativeUrl(location);
-
if (PROTOCOL_RELATIVE_URL_REGEX.test(location)) {
let path = resolvePath(location);
return path.pathname + path.search + path.hash;