Skip to content

fix(metadata): preserve URL instance pathname in alternates#91493

Open
mango766 wants to merge 1 commit intovercel:canaryfrom
mango766:fix/metadata-alternates-url-pathname
Open

fix(metadata): preserve URL instance pathname in alternates#91493
mango766 wants to merge 1 commit intovercel:canaryfrom
mango766:fix/metadata-alternates-url-pathname

Conversation

@mango766
Copy link

What?

Fix resolveAlternateUrl to preserve the full pathname of URL instances passed in metadata.alternates.languages, alternates.canonical, alternates.media, and alternates.types.

Why?

When users pass a URL object for alternate language URLs (e.g. new URL('/nl/blog', 'http://localhost:3000')), the URL's own pathname was being silently discarded. The old code did new URL(pathname, url), treating the user's URL as a base and resolving it against the current page's pathname — effectively replacing /nl/blog with /blog.

This means any locale prefix, subpath, or custom path encoded in the URL was lost. The workaround was to call .toString() on the URL, which shouldn't be necessary.

Reported by multiple users in #68351, confirmed still present as of v15.2.3.

How?

In resolveAlternateUrl, when the input is a URL instance, convert it to its href string before passing it downstream to resolveAbsoluteUrlWithPathname. This way the full path the user specified is treated as an absolute URL and preserved as-is.

Before (broken):

if (url instanceof URL) {
  const newUrl = new URL(pathname, url)  // ← overwrites url's own pathname
  // ...
  url = newUrl
}

After (fixed):

if (url instanceof URL) {
  url = url.href  // ← preserves url's full path
}

Test plan

  • Added test/e2e/app-dir/metadata/app/alternates/url-instance/page.tsx fixture that uses URL instances for canonical and languages
  • Added test case 'should preserve URL instance pathname in alternates' to the existing metadata e2e test suite
  • Existing alternate tests (relative paths, string URLs) continue to pass as before

Fixes #68351

…ing it

When a URL instance is passed for alternates.languages or
alternates.canonical, the resolveAlternateUrl function was using
`new URL(pathname, url)` which replaced the URL's own pathname with
the current page pathname. This caused URLs like
`new URL('/nl/blog', 'http://localhost:3000')` to lose their path
and always resolve to the page's own pathname.

Convert URL instances to their href string so
resolveAbsoluteUrlWithPathname treats them as absolute URLs and
preserves the full path the user intended.

Fixes vercel#68351
@nextjs-bot
Copy link
Collaborator

Allow CI Workflow Run

  • approve CI run for commit: 16b994e

Note: this should only be enabled once the PR is ready to go and can only be enabled by a maintainer

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug with metadata alternates when using URL

2 participants