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}`
}