前言:為什麼我的網站像個隱形人?
花三個月裝潢一間展示廳,燈光挑過、色票對過、接待人員的制服也訂做了。大門拉開那天,連空氣裡都飄著木頭香。
一個禮拜過去,門口羅雀。
跑去找負責發傳單的鄰長,他抽著菸搖頭:「喔,你有開店喔?我看你大門口有掛招牌,但走進去——不管是廁所、廚房還是臥房——長得全都一樣。我不知道怎麼介紹,就沒發。」
這不是笑話,是同業在 Next.js App Router 上踩過的真實場景。網站明明部署了,打開 Google Search Console 一看,除了首頁之外,/page1、/page2 連寫了一個月的部落格文章,全部沒被搜尋引擎收進去。
搜尋引擎是大多數網站流量的主要入口,相關產業統計可參考 Search Engine Journal 首頁 的 SEO 重要性專題報導。這篇文章將深入探討 SEO 索引災難的原因,並提供 6 個排查步驟與實戰清單,幫助你修復這些問題。
SEO 是什麼?從圖書館管理員的角度看搜尋引擎索引
如果你是剛接觸這個領域的人,可能會直接問:SEO 到底是什麼?為什麼工程師也要懂?
最直白的說法:SEO 是「討好圖書館管理員」的技術。
Google 是一座藏書幾千億本的圖書館。你網站上發布一個新頁面,等於印好一本新書送過去。三件事會發生:
- 爬蟲(Crawling):管理員派工讀生到倉庫把書搬回來。
- 索引(Indexing):管理員翻過,決定這本書要放到哪一區、貼什麼標籤。
- 排名(Ranking):讀者問「哪裡有可靠的 Next.js 教學」,管理員決定你的書擺在第幾順位。
這次要處理的問題出在第二步。Google 的管理員來過,但他拒絕把分頁放上書架——因為他覺得這些書「標示不清」。如果你想進一步了解 Google 索引運作原理,可參考 Google Search Central: How Search Works 。這一點強調了 SEO 索引災難的嚴重性,因為它直接影響網站的可見性。
災難現場:Google Search Console 的無情判決
實際打開 Google Search Console 的「網址審查(URL Inspection)」工具(官方說明文件 ),畫面讓人心涼。分頁狀態顯示:
> Google 選擇了與使用者不同的標準網址(Canonical URL)
白話翻譯:你跟 Google 說「嘿,這頁是聯絡我們喔」,Google 回你:「我看這頁跟首頁長得幾乎一樣,標頭也說它是首頁,所以我就把它當成首頁的複製品處理掉了。」
最後的結果:搜尋引擎眼裡,整個網站只剩「一頁」。其他所有頁面被歸類為重複內容,直接從索引裡剔除。這對排名是毀滅性的——Google 最厭惡的就是抄自己的網站。
案情分析:當所有孩子都長得跟爸爸一樣
追了一輪技術根源,問題出在 Next.js 的 Metadata 繼承機制。
先講個觀念:Canonical URL(標準網址)由 Google Search Central 官方說明 ,是告訴搜尋引擎「這是這個內容的標準版本」。Next.js Metadata API 處理 canonical 的官方文件見 generateMetadata API reference 。如果三個網址都連到同一篇文章,你得用 `` 明確告訴 Google:「這三個之中,A 才是正版,請索引它。」關於 Canonical 的正確設定方式,推薦參考 Google 搜尋中心:重複網址標準化 。
這次踩坑的位置在 app/layout.tsx,那段程式碼寫得很「貼心」:
// ❌ 錯誤示範 (app/layout.tsx)
export const metadata: Metadata = {
// ... 其他設定
alternates: {
canonical: "https://domain-name",
},
};
本意是想設定「網站主網域」。但 Next.js 的 layout.tsx 是所有頁面的共同父層,Metadata 會自動繼承下去。這一行等於強制讓每個子頁面額頭上都被刺青:「我是首頁」。
跑出來的結果就是這樣:
- 首頁:「我是首頁!」(Google:OK)
- 聯絡頁:「我是首頁!」(Google:分身?)
- 產品頁:「我是首頁!」(Google:又是分身,丟掉。)
技術瓶頸:Client Component 的限制
這時候有人會問:「那分頁自己改掉不就好了?」
這就碰到 Next.js App Router 的一條硬限制。為了讓網站有滑順的進場動畫跟互動,這次的分頁大多標註了 "use client",變成 Client Component。
兩者的差別很關鍵:
- Server Component:可以在伺服器端先把 Metadata(身分證)準備好,Google 爬蟲一來就讀得到。
- Client Component:負責在瀏覽器裡表演——動畫、互動、捲動特效都歸它管,但不支援
export const metadata。
換句話說,分頁因為要負責「表演」,所以無法自己遞出 Metadata,只能繼承 layout.tsx 塞給它的「首頁證件」。相關技術細節可參考 Next.js 官方 Metadata 概覽 。
解決方案:官方推薦的「Server Shell + Client Logic」模式
要修這個問題,業界有個常見的重構模式,叫「Server Shell + Client Logic」,相關實作可見 Vercel Academy: Dynamic Metadata Done Right 與 Next.js 官方 generateMetadata 文件 。概念很簡單:幫每個頁面請一個經紀人。
- 經紀人(Server Component):處理行政、遞名片(Metadata)、應付爬蟲。
- 藝人(Client Component):上台跳舞、做動畫、跟使用者互動。
步驟一:移除錯誤的家族刺青
回到 app/layout.tsx,把那行寫死的 canonical: "https://domain-name" 拿掉。讓孩子們有機會自己寫名字。
步驟二:拆分檔案
原本一個檔案,拆成兩個:
- 藝人(Client Component)——
client.tsx
只負責畫面和動畫,不碰 SEO。
// 📄 app/page1/client.tsx
"use client";
import { motion } from "framer-motion";
export default function Page1() {
return (
{/* 原本炫酷的互動介面 */}
);
}
- 經紀人(Server Component)——
page.tsx
該路徑的進入點,負責告訴 Google 這是哪一頁,再把藝人請出場。
// 📄 app/page1/page.tsx
import Page1 from "./client";
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "我是分頁",
description: "",
alternates: {
canonical: "https://domain-name/page1",
},
};
export default function ShowPage1() {
return ;
}
步驟三:動態文章怎麼辦?
部落格文章 /blog/[slug] 的網址會變,經紀人也得會變通——動態印名片。在 Next.js 15+ 中,params 是 Promise 型別,必須 await 才能取值,相關說明見 Next.js 官方 generateMetadata 文件 :
// app/blog/[slug]/page.tsx
import type { Metadata, ResolvingMetadata } from "next";
type Props = {
params: Promise<{ slug: string }>;
};
export async function generateMetadata(
{ params }: Props,
parent: ResolvingMetadata
): Promise {
const { slug } = await params;
return {
title: `文章標題 - ${slug}`,
alternates: {
canonical: `https://domain-name/blog/${slug}`,
},
};
}
如果你想了解結構化資料如何強化部落格文章的搜尋呈現,可參考 結構化數據 Schema 完整教學 。
結語:讓 Google 讀懂你的每一頁
做完這輪「整型手術」後,重新提交 Google Search Console 審查,所有頁面都拿到自己的身分證。索引狀態從灰燈轉綠燈那一刻,螢幕盯久了眼睛會痠,但心情是鬆一口氣的。
這次過程留下幾個值得帶走的觀察:
- SEO 不只是行銷團隊的事,寫程式的人本來就該懂一點。
- 在 Next.js App Router 裡,Server Component 跟 Client Component 的界線要畫清楚——前者管 SEO 與資料,後者管互動。
- 不要在全域 Layout 寫死 Canonical URL。這是同業常見的失誤,修正成本不高,但事前多看一眼能省下後面好幾週的 debug 時間。
網站不再當隱形人了。如果你也想順手檢查自己的內部連結與資訊架構是否健全,推薦閱讀 從 Silo 結構到內部連結:資訊地圖規劃指南 。如果你也是用 Next.js 開發,順手打開自己的 layout.tsx 檢查一下——別讓一行設定擋住你接下來半年的流量。
面對生成式引擎崛起的新時代,傳統 SEO 之外也開始衍生出 生成式引擎優化(GEO) 的新思維,值得一併關注。
6 個 SEO 索引排查步驟(工程師實戰清單)
這是 jxstamp 與 fordigE 內部用過的真實流程,按順序執行可以定位 90% 以上的 Next.js App Router 索引問題。
步驟 1:用 Google Search Console URL Inspection 工具確認被索引的 URL
登入 Google Search Console ,選左邊「網址審查」輸入你想查的 URL。點「要求檢索」讓 Google 重新抓取,再點「查看檢索的網頁」看 Google 看到的 HTML 是不是跟你預期一致。
如果 Google 看到的 head 跟你預期不同(例如 metadata 繼承首頁的標題),問題就在這。Next.js 官方 generateMetadata 文件 明確說明 metadata 會自動從 root layout 繼承到子頁面。
步驟 2:檢查 root layout 是否有 inheritance metadata
打開 app/layout.tsx,看有沒有這樣寫:
// ❌ 寫死的 canonical 會強制每個子頁面都變首頁
export const metadata: Metadata = {
alternates: { canonical: "https://domain-name" },
};
正解:把 layout 層級的 canonical 拿掉,讓每個 page 自己寫 canonical。見 Vercel Academy: Dynamic Metadata Done Right 的範例。
步驟 3:把 Client Component 拆出經紀人模式
每個 page 必須有 page.tsx(Server Component,負責 metadata)+ client.tsx(Client Component,負責互動)兩個檔案。Server Component 寫 export const metadata 才能讓 Google 讀到。
// app/about/page.tsx
import { Metadata } from 'next';
import AboutClient from './client';
export const metadata: Metadata = {
title: '關於我們 - 鍛碼匠',
description: '台中網頁設計公司介紹',
alternates: { canonical: 'https://fordige.com/about' },
};
export default function Page() {
return ;
}
步驟 4:設定 robots.txt 與 sitemap.xml
詳見 Next.js 官方 sitemap 文件 與 Next.js robots 文件 。
步驟 5:手動提交 sitemap 給 Google Search Console
到 Google Search Console Sitemaps 提交 https://fordige.com/sitemap.xml 。可以請 Google 重新索引。Google 官網說明在 Google Search Central: Build & submit sitemap 。
步驟 6:觀察 7-14 天等 Google 重新爬取
提交後 7-14 天用 Search Console 的「涵蓋範圍」報告看狀態。如果分頁開始被索引(綠色「已索引」),修復成功。如果仍顯示「已排除 → 已編入索引,但未選為標準網址」,代表繼承問題沒修乾淨,回步驟 2 再檢查一次。
實測成效
走完上述流程後,被 Google 索引的分頁數量從原本的少數幾頁擴張到數十頁,自然流量也明顯回升。具體的 Google 索引狀態說明 跟 URL Inspection 工具 都有詳述。若你想從更宏觀的視角評估網站對 AI 與搜尋引擎的友善程度,可參考 如何確保你的網站適應 AI 讀取的需求 。
延伸閱讀
這篇談的是 Next.js App Router 搜不到的真實原因:canonical 繼承導致 SEO 索引災難的修復實錄。如果你還在猶豫要不要做、或還沒決定走哪條路,
建議先看 現代網站架構指南 建立整體判斷框架,再回來對照這篇的細節。
- 網站想要被 Google 看懂?結構化數據 Schema 完整教學(2026)
- 如何確保你的網站適應 AI 讀取的需求
- SEO 真的死掉了嗎?生成式引擎優化(GEO)如何幫助您的品牌在 AI 時代
- 從 Silo 結構到內部連結:如何規劃讓搜尋引擎爬蟲著迷的資訊地圖?
下一步行動
- 檢查你的
layout.tsx檔案,確保沒有錯誤的 Canonical URL 設定。 - 針對每個頁面,使用 Google Search Console 的網址審查工具,確認索引狀態。
- 針對動態頁面,實作
generateMetadata函數,確保每個頁面都有正確的 Metadata。 - 參考 Next.js 官方文件 確保你了解 Metadata 的使用。
- 持續關注 SEO 的最佳實踐,定期檢查網站的可見性和性能。








