diff --git a/packages/browser-core/src/tools/utils/urlPolyfill.spec.ts b/packages/browser-core/src/tools/utils/urlPolyfill.spec.ts
index 4ca746ffb5..a8828737cf 100644
--- a/packages/browser-core/src/tools/utils/urlPolyfill.spec.ts
+++ b/packages/browser-core/src/tools/utils/urlPolyfill.spec.ts
@@ -29,6 +29,36 @@ describe('normalize url', () => {
// let's check for both.
expect(['file:///my/path', 'file://foo.com/my/path']).toContain(normalizeUrl('file://foo.com/my/path'))
})
+
+ describe('with a differing from the current path', () => {
+ // The browser resolves document-relative request URLs against the document base URI, not the
+ // page location. normalizeUrl must match that so the recorded URL pairs with its
+ // PerformanceResourceTiming entry.
+ let base: HTMLBaseElement
+
+ beforeEach(() => {
+ history.pushState({}, '', '/deep/route')
+ base = document.createElement('base')
+ base.href = '/'
+ document.head.appendChild(base)
+ })
+
+ afterEach(() => {
+ base.remove()
+ })
+
+ it('should resolve document-relative paths against the base URI', () => {
+ expect(normalizeUrl('api/foo')).toEqual(`${location.origin}/api/foo`)
+ })
+
+ it('should still resolve root-relative paths against the origin', () => {
+ expect(normalizeUrl('/api/foo')).toEqual(`${location.origin}/api/foo`)
+ })
+
+ it('should keep absolute urls unchanged', () => {
+ expect(normalizeUrl('https://foo.com/my/path')).toEqual('https://foo.com/my/path')
+ })
+ })
})
describe('isValidUrl', () => {
diff --git a/packages/js-core/src/util/urlPolyfill.ts b/packages/js-core/src/util/urlPolyfill.ts
index e30d87b177..168ff3ce6f 100644
--- a/packages/js-core/src/util/urlPolyfill.ts
+++ b/packages/js-core/src/util/urlPolyfill.ts
@@ -1,9 +1,17 @@
import type { GlobalObject } from './globalObject'
import { globalObject } from './globalObject'
-/** Resolves a URL against the current page location, returning a normalized absolute URL string. */
+/**
+ * Resolves a URL, returning a normalized absolute URL string.
+ *
+ * Document-relative inputs are resolved against `document.baseURI` (the ``, defaulting to
+ * the document location) to match how the browser resolves relative request URLs — so the URL
+ * recorded for a fetch/XHR matches the one actually requested. Falls back to `location.href` in
+ * environments without a document (workers, SSR). Absolute and root-relative inputs ignore the base
+ * and are unaffected.
+ */
export function normalizeUrl(url: string) {
- return buildUrl(url, globalObject.location?.href).href
+ return buildUrl(url, globalObject.document?.baseURI ?? globalObject.location?.href).href
}
/** Returns true if the given string is a valid URL. */