From fd6a715fec7a43e6584b20e419836aff0235f9bc Mon Sep 17 00:00:00 2001 From: Sebastian Beltran Date: Sun, 2 Aug 2026 17:16:25 -0500 Subject: [PATCH] feat: differentiate versioned pages metadata for search engines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pages under a version segment (/{lang}/4x|5x/, docs and API) now include the version in the title ("Using middleware · Express.js 4.x") and append a "Documentation for Express.js 4.x." sentence to the meta description, also reflected in the Open Graph and Twitter tags. This keeps the 4x and 5x pages from competing as near-duplicates in search results while both remain indexed with their own canonical. --- src/layouts/Layout.astro | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/layouts/Layout.astro b/src/layouts/Layout.astro index 948b0c121d..b648c9eb0b 100644 --- a/src/layouts/Layout.astro +++ b/src/layouts/Layout.astro @@ -23,10 +23,6 @@ const { canonicalPathname, } = Astro.props; -const pageTitle = title - ? `${title} · Express.js` - : 'Express.js · Node.js web application framework'; - const pathname = Astro.url.pathname.replace(/\/$/, ''); const segments = pathname.split('/').filter(Boolean); const langSegment = segments[0] || 'en'; @@ -35,6 +31,18 @@ const isApi = restSegments[0] === 'api' || (restSegments[1] === 'api' && /^\dx$/.test(restSegments[0] ?? '')); const isBlogOrApi = restSegments[0] === 'blog' || isApi; +// Versioned pages carry the version in title/description so search engines +// don't treat 4x and 5x as duplicates. +const versionSegment = /^\dx$/.test(restSegments[0] ?? '') ? restSegments[0] : undefined; +const versionLabel = versionSegment && `Express.js ${versionSegment[0]}.x`; + +const pageTitle = title + ? `${title} · ${versionLabel ?? 'Express.js'}` + : 'Express.js · Node.js web application framework'; +const pageDescription = versionLabel + ? `${description} Documentation for ${versionLabel}.` + : description; + // Blog and API content is not translated, so their canonical is the English // page and they emit no hreflang alternates. let canonicalPath = canonicalPathname ?? Astro.url.pathname; @@ -94,7 +102,7 @@ const lang = getLangFromUrl(Astro.url); - + @@ -128,7 +136,7 @@ const lang = getLangFromUrl(Astro.url); - + {authors?.map((author) => )} @@ -136,7 +144,7 @@ const lang = getLangFromUrl(Astro.url); - +