seo: /v2 and /v2-5 canonical to the home and out of the sitemap

Superseded release pages that were taking two brand sitelink slots (5.9k
impressions, 29 clicks). They still render for old links; hreflang is
only emitted for a page that is its own canonical (prerender + useSEO).
This commit is contained in:
David Montero 2026-08-18 17:29:54 +02:00
parent 18b133311e
commit 6f19cf9068
4 changed files with 21 additions and 13 deletions

View File

@ -87,16 +87,6 @@
<changefreq>monthly</changefreq>
<priority>0.85</priority>
</url>
<url>
<loc>https://velxio.dev/v2/</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/v2-5/</loc>
<changefreq>monthly</changefreq>
<priority>0.9</priority>
</url>
<url>
<loc>https://velxio.dev/v3/</loc>
<changefreq>weekly</changefreq>

View File

@ -147,7 +147,11 @@ try {
} else {
html = html.replace('</head>', ` ${canonicalTag}\n </head>`);
}
html = withHreflang(html, route.path);
// hreflang only for a page that is its own canonical; a page pointing
// its canonical elsewhere (/v2 -> /) must not declare alternates.
if (withSlash(seoMeta.url) === withSlash(`${DOMAIN}${route.path}`)) {
html = withHreflang(html, route.path);
}
// Replace OG tags
html = html.replace(

View File

@ -236,24 +236,31 @@ export const SEO_ROUTES: SeoRoute[] = [
{
path: '/v2',
priority: 0.9,
// Superseded release page. Canonical points at the home (seoMeta.url)
// and noindex keeps it out of the sitemap: /v2 and /v2-5 were taking
// two brand sitelink slots (5.9k impressions, 29 clicks) that the
// editor and the simulator landings should hold. The page still
// renders for old links.
noindex: true,
changefreq: 'monthly',
seoMeta: {
title: 'Velxio 2.0 — Multi-Board Embedded Simulator | ESP32, Raspberry Pi, Arduino, RISC-V',
description:
'Velxio 2.0 is here. Simulate Arduino, ESP32, Raspberry Pi Pico, and Raspberry Pi 3 in your browser. 19 boards, 68+ examples, realistic sensor simulation. Free and open-source.',
url: `${DOMAIN}/v2`,
url: `${DOMAIN}/`,
},
},
{
path: '/v2-5',
priority: 0.9,
noindex: true, // see /v2
changefreq: 'monthly',
seoMeta: {
title:
'Velxio 2.5 — Arduino + SPICE Analog Circuit Simulator in Your Browser | ngspice-WASM',
description:
'Velxio 2.5 brings real-time analog circuit simulation via ngspice-WASM. Hybrid digital + analog co-simulation: resistors, capacitors, inductors, op-amps, transistors, voltmeters, ammeters — wired to Arduino, ESP32, RP2040 GPIO/ADC. 40+ circuit examples. Free and open-source.',
url: `${DOMAIN}/v2-5`,
url: `${DOMAIN}/`,
},
},
{

View File

@ -70,6 +70,13 @@ function localeLinks(url: string): { canonical: string; alternates: { hreflang:
return { canonical: canonicalDefault, alternates: [] };
}
if (!isLocalizedRoute(strippedPath)) return { canonical: canonicalDefault, alternates: [] };
// A page whose canonical is another page (/v2 -> /) keeps that canonical
// as given and declares no alternates.
if (typeof window !== 'undefined') {
const herePath = stripLocaleFromPath(window.location.pathname).replace(/\/+$/, '') || '/';
const urlPath = strippedPath.replace(/\/+$/, '') || '/';
if (herePath !== urlPath) return { canonical: canonicalDefault, alternates: [] };
}
const current = typeof window !== 'undefined' ? getLocaleFromPath(window.location.pathname) : DEFAULT_LOCALE;
const variant = (l: (typeof LOCALES)[number]) => withTrailingSlash(`${DOMAIN}${localizedPath(strippedPath, l)}`);
return {