Skip to content

fix(language-selector): keep the query string and hash through redirects - #2154

Merged
markdumay merged 1 commit into
mainfrom
fix/canonical-redirect-query-string
Sep 3, 2026
Merged

fix(language-selector): keep the query string and hash through redirects#2154
markdumay merged 1 commit into
mainfrom
fix/canonical-redirect-query-string

Conversation

@markdumay

Copy link
Copy Markdown
Collaborator

Problem

The canonical guard in assets/js/critical/languageSelector.js compares the full window.location.href against the <link rel="canonical"> href and hard-navigates on any difference:

if ((alias !== '') && (window.location.href !== alias)) {
  window.location.href = alias
}

The canonical never carries a query string, so any URL that does looks like a mismatch. The visitor is bounced to the bare canonical URL and the parameters are gone — utm_source, utm_medium, utm_campaign, plus gclid, fbclid, msclkid, hsCtaTracking. Anchor deep links (#section) are dropped the same way.

This is not limited to alias pages. When main.enableLanguageSelectionStorage is true this script replaces static/js/alias.js and ships in the critical bundle on every page, so the redirect fires site-wide. The only URL that escapes is ?force=true, via the early return above.

static/js/alias.js — the redirect this script stands in for — already gets it right:

const params = window.location.search + window.location.hash
window.location = alias + params

Reported by a marketing team whose campaign links lost their UTM parameters before GA4 could record the attribution.

Fix

  • Decide on the location alone: compare origin + pathname, so a page that is already canonical never redirects. This strictly reduces redirects, so it cannot introduce a new one.
  • When a redirect is warranted (a genuine alias page), carry location.search + location.hash to the target, matching alias.js.
  • Apply the same to applyLanguage, which dropped parameters when switching translations.
  • Fix applyLanguage's bare-folder branch, which compared a full href against a path (window.location.href !== '/nl/') — a comparison that can never be equal, so the guard never suppressed anything.

Behaviour

case before after
plain canonical visit stays put stays put
UTM campaign link redirects, params lost stays put
gclid / ad click id redirects, params lost stays put
#anchor deep link redirects, anchor lost stays put
alias page redirects, params lost redirects, params kept

Alias redirection is preserved — it just no longer discards the payload.

Testing

npm test passes (eslint, stylelint, markdownlint, template tests).

The canonical guard compared the full `window.location.href` against the
canonical link and hard-navigated on any difference. A query string or a
hash is enough to make those strings differ, so every visit carrying
campaign parameters -- utm_*, gclid, fbclid, msclkid -- was bounced to the
bare canonical URL before an analytics tag could read them, and anchor deep
links were dropped the same way. Sites that enable the language selector
were affected on every page, since this script replaces static/js/alias.js
and runs site-wide rather than only on alias pages.

Decide on the location alone: compare origin and pathname, so a page that
is already canonical never redirects. When a redirect is warranted, carry
`location.search + location.hash` to the target, matching what
static/js/alias.js already does. Apply the same treatment to the language
switch, which dropped parameters when moving between translations, and fix
its bare-folder branch to compare pathnames rather than a full href against
a path -- a comparison that could never match.
@netlify

netlify Bot commented Sep 3, 2026

Copy link
Copy Markdown

Deploy Preview for gethinode-demo ready!

Name Link
🔨 Latest commit 7d04391
🔍 Latest deploy log https://app.netlify.com/projects/gethinode-demo/deploys/6a996b141aabe800083ca413
😎 Deploy Preview https://deploy-preview-2154--gethinode-demo.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@markdumay
markdumay merged commit 61e86a4 into main Sep 3, 2026
17 checks passed
@markdumay
markdumay deleted the fix/canonical-redirect-query-string branch September 3, 2026 12:51
@markdumay

Copy link
Copy Markdown
Collaborator Author

🎉 This PR is included in version 3.23.11 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant