From 4aa80d12556d04f70f960554e6bf458bc750b018 Mon Sep 17 00:00:00 2001 From: Florent Tapponnier Date: Fri, 26 Jun 2026 17:08:18 +0200 Subject: [PATCH] fix(seo): canonical points to /chains/ when provider IS a chain /products/eth-usd, /products/bnb-usd, /products/avalanche-official etc. resolve to a canonical provider whose slug matches a chain (ethereum, bnb, avalanche...). The previous canonical /products/ then 308s to /chains/, so Ahrefs + GSC flag 'canonical points to redirect' and Google may split rank instead of consolidating on the final URL. Now we check the chain registry and short-circuit the canonical (+ OG url) straight to /chains/ for chain-shaped providers. Non-chain providers (helius, dRPC, mobula) keep /products/. --- src/app/products/[slug]/page.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/app/products/[slug]/page.tsx b/src/app/products/[slug]/page.tsx index 6771b19c..f0333aa2 100644 --- a/src/app/products/[slug]/page.tsx +++ b/src/app/products/[slug]/page.tsx @@ -3,6 +3,7 @@ import { notFound } from "next/navigation"; import Link from "next/link"; import { ArrowLeft, ArrowUpRight } from "lucide-react"; import { getProvider } from "@/lib/providers"; +import { CHAIN_BY_SLUG } from "@/lib/chains"; import { ProviderLogo } from "@/components/provider-logo"; import { CATEGORY_COLOR } from "@/lib/category-colors"; import { fmtUnit } from "@/lib/format"; @@ -84,12 +85,20 @@ export async function generateMetadata({ ? `${reg.description.replace(/[.!?]?$/, ".")} Live performance across ${benchCount} OpenChainBench ${benchWord}${winSuffix}.` : fallbackDescription; - const url = `${SITE.url}/products/${p.slug}`; + // When the resolved provider slug is actually a chain (e.g. /products/eth-usd + // aliases to /products/ethereum which 308s to /chains/ethereum), point + // canonical straight to the final 200 surface. Otherwise Ahrefs + GSC + // flag "canonical points to redirect" and Google may split rank between + // source + final instead of consolidating. + const isChain = CHAIN_BY_SLUG.has(p.slug); + const canonicalUrl = isChain + ? `${SITE.url}/chains/${p.slug}` + : `${SITE.url}/products/${p.slug}`; return { title, description, - alternates: { canonical: url }, - openGraph: { title, description, type: "profile", url }, + alternates: { canonical: canonicalUrl }, + openGraph: { title, description, type: "profile", url: canonicalUrl }, twitter: { card: "summary_large_image", title, description }, }; }