NextJS+react-i18n国际化下Canonical URL生产环境异常求助
解决NextJS 14.2.3 + Vercel部署下默认语言Canonical URL带前缀的问题
核心问题
Vercel生产环境下,默认语言(da)的路由被自动加上了/da前缀,导致canonical URL不符合预期,而本地测试正常。这通常是因为路由处理逻辑在生产环境的解析差异,或是metadata生成时未区分默认语言和非默认语言的路径规则。
解决方案步骤
1. 确认NextJS国际化配置
先检查next.config.js的i18n配置,确保默认语言设置正确:
/** @type {import('next').NextConfig} */ const nextConfig = { i18n: { locales: ['da', 'en'], defaultLocale: 'da', localeDetection: false, // 关闭自动语言检测,避免不必要的跳转 }, } module.exports = nextConfig
2. 用Middleware统一路由规则
添加middleware.js,将默认语言的带前缀路径重定向到无前缀版本,同时确保非默认语言保留前缀:
import { NextResponse } from 'next/server' import Negotiator from 'negotiator' import { match } from '@formatjs/intl-localematcher' const locales = ['da', 'en'] const defaultLocale = 'da' function getLocale(request) { const headers = {} request.headers.forEach((val, key) => (headers[key] = val)) const languages = new Negotiator({ headers }).languages() return match(languages, locales, defaultLocale) } export function middleware(request) { const pathname = request.nextUrl.pathname // 已包含locale前缀的路径直接放行 if (locales.some(locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`)) { // 如果是默认语言的前缀路径,重定向到无前缀版本 if (pathname.startsWith(`/${defaultLocale}`)) { const newPath = pathname.replace(`/${defaultLocale}`, '') || '/' return NextResponse.redirect(new URL(newPath, request.url)) } return } // 无前缀路径:根据用户语言偏好跳转,默认语言直接保留无前缀 const locale = getLocale(request) if (locale === defaultLocale) { return NextResponse.next() } else { return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url)) } } export const config = { matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'], }
3. 精准生成Canonical URL
在App Router的layout.js或page.js中,通过generateMetadata函数,根据当前locale是否为默认语言,构建正确的绝对路径Canonical:
import { headers } from 'next/headers' export async function generateMetadata({ params, locale }) { const defaultLocale = 'da' const host = headers().get('host') const protocol = process.env.NODE_ENV === 'production' ? 'https' : 'http' // 构建路径:默认语言去掉前缀,非默认语言保留前缀 let canonicalPath = '' if (locale !== defaultLocale) { canonicalPath = `/${locale}` } // 拼接子页面路径 if (params?.slug) { canonicalPath += `/${params.slug}` } const canonicalUrl = `${protocol}://${host}${canonicalPath}` return { alternates: { canonical: canonicalUrl, languages: { 'da': `${protocol}://${host}${params?.slug ? `/${params.slug}` : ''}`, 'en': `${protocol}://${host}/en${params?.slug ? `/${params.slug}` : ''}`, }, }, // 其他元数据配置... } }
额外注意事项
- 避免使用相对路径(
./或/)生成canonical,绝对路径能确保本地和生产环境解析一致。 - 部署到Vercel后,清除缓存重新部署一次,确保路由规则生效。
内容的提问来源于stack exchange,提问作者Kalrin
相关产品推荐
相关产品推荐

