Sitemap / Canonical / Hreflang 一致性|SEO Metadata
canonical domain、sitemap、hreflang、trailing slash 四者必須完全一致。只要其中一項混用 www / non-www、漏掉語系、或 URL 格式不同,Google 就會把索引訊號拆散,導致收錄異常。
Canonical Domain 統一
只能有一個正式網域
先選定 canonical domain,所有 SEO 輸出都跟著它。以下位置不得混用:
site.urlruntimeConfig.public.siteUrlNUXT_PUBLIC_SITE_URL環境變數<link rel="canonical">og:url/ Twitter URL- JSON-LD
url/@id - sitemap index 的
<loc> - child sitemap 的
<loc> robots.txt的 Sitemap URL- API 回傳的公開分享 URL
Nuxt 設定範例
const canonicalSiteUrl = process.env.NUXT_PUBLIC_SITE_URL || 'https://www.example.com'
export default defineNuxtConfig({ site: { url: canonicalSiteUrl, name: '網站名稱', description: '全站描述', defaultLocale: 'zh-TW', trailingSlash: true }, runtimeConfig: { public: { siteUrl: canonicalSiteUrl } }})常見錯誤:fallback 值用了 non-www。一旦 production 環境漏設環境變數,JSON-LD、canonical、sitemap 全部會指到錯的網域。
// ❌ fallback 是 non-www,production 漏 env 就會裂開config.public.siteUrl || 'https://example.com'部署層規則
- non-www 必須 301/308 到 www(或反向),但只能選一種。
- Google Search Console 驗證 domain property,或至少同時驗 www / non-www。
- sitemap 只提交 canonical domain 版本。
Sitemap 品質 SOP
基本原則
- sitemap 只放「可索引」頁面。
- sitemap URL 必須是最終 URL,不應回 3xx。
- sitemap 不能包含 noindex 頁面。
- sitemap 不能包含登入後頁、付款結果頁、PWA offline fallback、API route。
- child sitemap 必須在 production 可直接
200 XML。
Nuxt 設定範例
sitemap: { exclude: [ '/console', '/offline', '/payment/**', '/payment/result', '/account/**', '/history/**', '/auth/**', '/login', '/register', '/credits/**', '/payment-success', '/checkout/**', '/api/**' ]}Nuxt Content 動態路由
公開內容頁需要主動進 sitemap。生成時應讀取 content collection:
- route path 以實際公開 URL 為準。
lastmod優先用updatedAt,再用date/publishedAt。- 過濾
draft: true、private: true、disabled: true。
Sitemap index 與 child sitemap
若使用 i18n 分 child sitemap:
/sitemap_index.xml/__sitemap__/zh-TW.xml/__sitemap__/ja-JP.xml/__sitemap__/en-US.xml必查項目:
- sitemap index 是
200且 Content-Type 是 XML。 - 每個 child sitemap 都是
200且 Content-Type 是 XML。 - sitemap index 的 child
<loc>使用 canonical domain。 - child sitemap 內 URL 使用 canonical domain。
- Googlebot UA 不被 Cloudflare challenge。
- Nitro 能正確 serve server route,不回 SPA HTML。
如果 production 對 child sitemap 支援不穩,短期可退回單一 /sitemap.xml,先恢復 Google 抓取能力,再優化 sitemap index。
多語系 i18n hreflang
路由策略必須先定義
範例策略:
zh-TW: / (預設語系,無 prefix)ja-JP: /jaen-US: /en不要產生不存在的 /zh-TW、/ja-JP、/en-US 路由。若舊工具可能打到完整 locale code,加 alias redirects:
routeRules: { '/zh-TW': { redirect: { to: '/', statusCode: 301 } }, '/zh-TW/': { redirect: { to: '/', statusCode: 301 } }, '/ja-JP': { redirect: { to: '/ja', statusCode: 301 } }, '/ja-JP/': { redirect: { to: '/ja', statusCode: 301 } }, '/en-US': { redirect: { to: '/en', statusCode: 301 } }, '/en-US/': { redirect: { to: '/en', statusCode: 301 } }}hreflang 範例
<link rel="alternate" hreflang="zh-TW" href="https://www.example.com/cards/major/the-fool/" /><link rel="alternate" hreflang="ja-JP" href="https://www.example.com/ja/cards/major/the-fool/" /><link rel="alternate" hreflang="en-US" href="https://www.example.com/en/cards/major/the-fool/" /><link rel="alternate" hreflang="x-default" href="https://www.example.com/cards/major/the-fool/" />hreflang 規則
- HTML head 有 hreflang,不只 sitemap 有 alternate。
- 每頁包含 self reference。
- 有
x-default。 html lang跟當前語系一致。- canonical 指向當前語系自己的 canonical URL。
- 不混用
zh/zh-TW、ja/ja-JP、en/en-US。 - 只有實際存在或可正常 fallback / redirect 的語系 URL 才產生 alternate。
Trailing Slash 一致性
先選一種策略(有尾斜線或無尾斜線),然後全站一致。
若 production final URL 帶尾斜線:
https://www.example.com/pricing/則 canonical、sitemap、hreflang、JSON-LD 都要帶尾斜線。不要讓 sitemap URL 回 301/308。
Nuxt Content 注意事項
Nuxt Content 的 path 通常不含尾斜線,但正式 URL 可能含尾斜線。動態頁查 content 時要正規化 slug:
const slugPath = computed(() => { const s = route.params.slug return Array.isArray(s) ? s.filter(Boolean).join('/') : String(s || '').replace(/^\/+|\/+$/g, '')})
const { data: article } = await useAsyncData( `article-${slugPath.value.replace(/\//g, '-')}`, () => queryCollection('content') .where('path', '=', `/learn/${slugPath.value}`) .first(), { watch: [slugPath] })必測:/learn/history/article-slug 和 /learn/history/article-slug/ 兩者都應 SSR 找到內容,不可一個正常、一個「找不到文章」。
驗證指令
確認不回 301
Trailing slash 檢查
curl -I https://www.example.com/pricingcurl -I https://www.example.com/pricing/確認不含禁止頁面
Sitemap 品質檢查
# 不應出現 noindex / 私有路由curl -L https://www.example.com/__sitemap__/zh-TW.xml \ | grep -E "/offline|/payment|/account|/history|/login|/register"
# 應包含公開內容頁curl -L https://www.example.com/__sitemap__/zh-TW.xml \ | grep "/cards/major/"
# 不應混用 non-wwwcurl -L https://www.example.com/__sitemap__/zh-TW.xml \ | grep "https://example.com"完成標準
Checklist
Sitemap / Canonical / Hreflang 一致性
-
site.url/runtimeConfig.public.siteUrl/ env 統一使用 canonical domain - sitemap index 200 XML
- child sitemap 200 XML
- sitemap 不含 noindex / offline / 付款 / 會員 / 登入 / API 頁
- sitemap URL 不回 3xx / 4xx
- Nuxt Content 公開頁進 sitemap,draft/private 排除
- hreflang 有 self reference 和 x-default
- hreflang URL 與實際路由一致
- canonical 指向當前語系自己的 URL
- trailing slash 策略全站一致
- Nuxt Content 有無尾斜線都能查到內容