diff --git a/.changeset/witty-carrots-notice.md b/.changeset/witty-carrots-notice.md
new file mode 100644
index 000000000000..7c0f5cd7de08
--- /dev/null
+++ b/.changeset/witty-carrots-notice.md
@@ -0,0 +1,5 @@
+---
+'@sveltejs/kit': patch
+---
+
+Only normalise internal URLs
diff --git a/packages/kit/src/runtime/client/client.js b/packages/kit/src/runtime/client/client.js
index bd07f012ba6f..16a2889d61ad 100644
--- a/packages/kit/src/runtime/client/client.js
+++ b/packages/kit/src/runtime/client/client.js
@@ -238,6 +238,10 @@ export function create_client({ target, session, base, trailing_slash }) {
return false; // unnecessary, but TypeScript prefers it this way
}
+ // if this is an internal navigation intent, use the normalized
+ // URL for the rest of the function
+ url = intent?.url || url;
+
// abort if user navigated during update
if (token !== current_token) return false;
@@ -891,9 +895,12 @@ export function create_client({ target, session, base, trailing_slash }) {
const params = route.exec(path);
if (params) {
- const id = normalize_path(url.pathname, trailing_slash) + url.search;
+ const normalized = new URL(
+ url.origin + normalize_path(url.pathname, trailing_slash) + url.search + url.hash
+ );
+ const id = normalized.pathname + normalized.search;
/** @type {import('./types').NavigationIntent} */
- const intent = { id, route, params: decode_params(params), url };
+ const intent = { id, route, params: decode_params(params), url: normalized };
return intent;
}
}
@@ -930,9 +937,6 @@ export function create_client({ target, session, base, trailing_slash }) {
return;
}
- const pathname = normalize_path(url.pathname, trailing_slash);
- const normalized = new URL(url.origin + pathname + url.search + url.hash);
-
update_scroll_positions(current_history_index);
accepted();
@@ -940,12 +944,12 @@ export function create_client({ target, session, base, trailing_slash }) {
if (started) {
stores.navigating.set({
from: current.url,
- to: normalized
+ to: url
});
}
await update(
- normalized,
+ url,
redirect_chain,
false,
{
@@ -954,7 +958,7 @@ export function create_client({ target, session, base, trailing_slash }) {
details
},
() => {
- const navigation = { from, to: normalized };
+ const navigation = { from, to: url };
callbacks.after_navigate.forEach((fn) => fn(navigation));
stores.navigating.set(null);
diff --git a/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte b/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte
index b363f6435561..1c2295b61be0 100644
--- a/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte
+++ b/packages/kit/test/apps/basics/src/routes/routing/slashes/index.svelte
@@ -1,3 +1,8 @@
+
+
/routing/
/routing/?
-/routing/?foo=bar
\ No newline at end of file
+/routing/?foo=bar
+external
diff --git a/packages/kit/test/apps/basics/test/client.test.js b/packages/kit/test/apps/basics/test/client.test.js
index b64954822ca2..0a491d5fdf68 100644
--- a/packages/kit/test/apps/basics/test/client.test.js
+++ b/packages/kit/test/apps/basics/test/client.test.js
@@ -1,5 +1,5 @@
import { expect } from '@playwright/test';
-import { test } from '../../../utils.js';
+import { start_server, test } from '../../../utils.js';
/** @typedef {import('@playwright/test').Response} Response */
@@ -544,6 +544,24 @@ test.describe('Routing', () => {
expect(await page.textContent('#window-hash')).toBe('#target');
expect(await page.textContent('#page-url-hash')).toBe('#target');
});
+
+ test('does not normalize external path', async ({ page }) => {
+ const urls = [];
+
+ const { port, close } = await start_server((req, res) => {
+ urls.push(req.url);
+ res.end('ok');
+ });
+
+ try {
+ await page.goto(`/routing/slashes?port=${port}`);
+ await page.click(`a[href="http://localhost:${port}/with-slash/"]`);
+
+ expect(urls).toEqual(['/with-slash/']);
+ } finally {
+ await close();
+ }
+ });
});
test.describe('Shadow DOM', () => {