diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index c5b6dbd9..77baaf11 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -5,7 +5,7 @@ import { getBenchmarks } from "@/data/benchmarks"; import { loadAllAlternatives } from "@/lib/alternatives"; import { loadAllAnswers } from "@/lib/answers"; import { CHAINS, getBenchmarksForChain } from "@/lib/chains"; -import { getProviderSlugs } from "@/lib/providers"; +import { getProvider, getProviderSlugs } from "@/lib/providers"; import { SITE } from "@/data/site"; import type { Benchmark } from "@/types/benchmark"; import type { Answer } from "@/lib/answers"; @@ -190,7 +190,23 @@ async function buildFullSitemap(): Promise { return entries; }); - const providerRoutes: MetadataRoute.Sitemap = providerSlugs.map((slug) => ({ + // Validate every slug against getProvider() so the sitemap can never + // ship a /products/ URL that the page would 404 on. The page + // calls `getProvider(slug)` and renders notFound() when it returns + // undefined, so this is the exact filter generateStaticParams would + // need to apply if it were prerendering the route. Without this + // guard, Google indexed soft 404s for slugs the sitemap claimed + // existed (quicknode, coingecko, infura, ankr were all flagged). + const validatedSlugs = ( + await Promise.all( + providerSlugs.map(async (slug) => { + const p = await getProvider(slug); + return p ? slug : null; + }), + ) + ).filter((s): s is string => s !== null); + + const providerRoutes: MetadataRoute.Sitemap = validatedSlugs.map((slug) => ({ url: `${SITE.url}/products/${slug}`, lastModified: providerLastRun.get(slug.toLowerCase()) ?? catalogTs, changeFrequency: "daily",