From 7d38452e90bccdeb3a3faaaad3ae8bb0c27c38b7 Mon Sep 17 00:00:00 2001 From: "paperclip-resolver[bot]" <3736210+paperclip-resolver[bot]@users.noreply.github.com> Date: Thu, 21 May 2026 15:00:26 -0400 Subject: [PATCH] fix(seo): enable trailingSlash for consistent canonical/hreflang/sitemap URLs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Semrush flagged 9 hreflang conflicts because Vercel serves both /en and /en/ as 200 (no redirect), so whichever form tags use conflicts with the alternate crawled URL. Fix: enable trailingSlash: true in Next.js config so Vercel redirects /en → /en/ (308). Update page.tsx and sitemap generator to produce trailing-slash URLs consistently across: - - - sitemap.xml and Build tested locally — static export now generates directory-based index.html files (en/index.html instead of en.html). --- app/[lang]/[[...mdxPath]]/page.tsx | 4 ++-- next.config.mjs | 1 + scripts/generate-sitemap.mjs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/[lang]/[[...mdxPath]]/page.tsx b/app/[lang]/[[...mdxPath]]/page.tsx index 289accf..23603bf 100644 --- a/app/[lang]/[[...mdxPath]]/page.tsx +++ b/app/[lang]/[[...mdxPath]]/page.tsx @@ -10,8 +10,8 @@ const DEFAULT_LOCALE = 'en' export async function generateMetadata(props) { const params = await props.params const { metadata } = await importPage(params.mdxPath, params.lang) - const path = params.mdxPath ? `/${params.lang}/${params.mdxPath.join('/')}` : `/${params.lang}/` - const subPath = params.mdxPath ? `/${params.mdxPath.join('/')}` : '/' + const path = params.mdxPath ? `/${params.lang}/${params.mdxPath.join('/')}/` : `/${params.lang}/` + const subPath = params.mdxPath ? `/${params.mdxPath.join('/')}/` : '/' const languages: Record = {} for (const l of LOCALES) { languages[l] = `https://docs.sharpapi.io/${l}${subPath}` diff --git a/next.config.mjs b/next.config.mjs index deeac3d..2c2c1e3 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,6 +7,7 @@ const withNextra = nextra({ export default withNextra({ output: 'export', + trailingSlash: true, images: { unoptimized: true }, // Nextra reads i18n config, extracts locales, then removes it (App Router compatible) i18n: { diff --git a/scripts/generate-sitemap.mjs b/scripts/generate-sitemap.mjs index 544cfad..f3669ae 100644 --- a/scripts/generate-sitemap.mjs +++ b/scripts/generate-sitemap.mjs @@ -64,7 +64,7 @@ async function lastmod(file) { } function url(locale, route) { - const path = route ? `/${locale}/${route}` : `/${locale}` + const path = route ? `/${locale}/${route}/` : `/${locale}/` return `${HOST}${path}` }