From 2d37a5c16ed555370ddd6345d806a8f3987ec522 Mon Sep 17 00:00:00 2001 From: airano Date: Sun, 15 Feb 2026 17:27:16 +0330 Subject: [PATCH] fix(seo): generate correct sitemap URLs for default locale Sitemap was generating /en/* URLs which redirect to unprefixed paths due to localePrefix: 'as-needed', causing Google Search Console "Page with redirect" warnings and wasting crawl budget. Co-Authored-By: Claude Opus 4.6 --- apps/web/app/sitemap.ts | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/apps/web/app/sitemap.ts b/apps/web/app/sitemap.ts index 7f9ed9e..075caa6 100644 --- a/apps/web/app/sitemap.ts +++ b/apps/web/app/sitemap.ts @@ -5,6 +5,13 @@ const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || 'https://skills.palebluedot.live'; const locales = ['en', 'fa'] as const; +const DEFAULT_LOCALE = 'en'; + +/** Get canonical URL path for a locale (no prefix for default locale, matching localePrefix: 'as-needed') */ +function canonicalPath(locale: string, route: string): string { + if (locale === DEFAULT_LOCALE) return route || '/'; + return `/${locale}${route}`; +} const staticRoutes = [ '', @@ -27,7 +34,8 @@ const staticRoutes = [ ]; function makeEntry( - path: string, + locale: string, + route: string, options?: { lastModified?: Date; changeFrequency?: MetadataRoute.Sitemap[number]['changeFrequency']; @@ -35,17 +43,13 @@ function makeEntry( } ): MetadataRoute.Sitemap[number] { return { - url: `${BASE_URL}${path}`, + url: `${BASE_URL}${canonicalPath(locale, route)}`, lastModified: options?.lastModified, changeFrequency: options?.changeFrequency, priority: options?.priority, alternates: { languages: Object.fromEntries( - locales.map((locale) => { - // For root path, use /en or /fa; for others, prefix with locale - const localePath = path.replace(/^\/(en|fa)/, `/${locale}`); - return [locale, `${BASE_URL}${localePath}`]; - }) + locales.map((l) => [l, `${BASE_URL}${canonicalPath(l, route)}`]) ), }, }; @@ -64,10 +68,9 @@ export default async function sitemap(): Promise { // Static pages (for each locale) for (const locale of locales) { for (const route of staticRoutes) { - const path = `/${locale}${route}`; const isHome = route === ''; entries.push( - makeEntry(path, { + makeEntry(locale, route, { changeFrequency: isHome ? 'daily' : 'weekly', priority: isHome ? 1.0 : 0.5, }) @@ -82,7 +85,7 @@ export default async function sitemap(): Promise { for (const skill of allSkills) { for (const locale of locales) { entries.push( - makeEntry(`/${locale}/skill/${skill.id}`, { + makeEntry(locale, `/skill/${skill.id}`, { lastModified: skill.updatedAt, changeFrequency: 'weekly', priority: 0.7, @@ -103,7 +106,7 @@ export default async function sitemap(): Promise { for (const [owner, lastModified] of uniqueOwners) { for (const locale of locales) { entries.push( - makeEntry(`/${locale}/owner/${owner}`, { + makeEntry(locale, `/owner/${owner}`, { lastModified, changeFrequency: 'weekly', priority: 0.6, @@ -121,7 +124,7 @@ export default async function sitemap(): Promise { for (const category of allCategories) { for (const locale of locales) { entries.push( - makeEntry(`/${locale}/browse?category=${category.slug}`, { + makeEntry(locale, `/browse?category=${category.slug}`, { changeFrequency: 'weekly', priority: 0.5, })