同一篇文章寫了繁體中文與英文兩個版本,結果 Google 在台灣的搜尋結果裡卻丟出英文頁,或是把兩個版本當成重複內容、只索引其中一個。這不是 Google 出錯,而是網站沒有用 hreflang 設定告訴搜尋引擎「這幾個 URL 是同一份內容的不同語言版本,請按使用者的語言與地區挑對的那一個」。
多語系網站最常見的兩個災情,一是地區版本互搶排名(同一組關鍵字,英文頁和中文頁彼此競食,誰也排不上去),二是錯誤索引(Google 收錄了不該對台灣讀者顯示的版本)。這兩件事的根源往往是同一個:hreflang 沒裝、裝錯,或是裝了但少了關鍵的回指與自我參照標籤。
這篇會把 hreflang 的運作原理講清楚,拆解最容易壞掉的幾種寫法,再針對 WordPress 用 Polylang、WPML 與 Yoast、Rank Math 的實際設定路徑逐一示範,最後給一套可以照著跑的驗證流程。繁體中文站、面向台灣讀者的情境會特別點出來,因為 zh-TW 與 zh-CN 的取捨,多數英文教學都沒講到。
hreflang 標籤是什麼,搜尋引擎怎麼用它
hreflang 是一段放在頁面 <head> 裡的 HTML 標註,用來宣告「這個 URL 是哪一種語言、哪一個地區的版本」,並列出同一份內容的其他語言版本在哪裡。Google 讀到這組標註後,會在搜尋結果裡依使用者的語言偏好與所在地區,挑出最相符的那個版本顯示。
一組典型的 hreflang,假設同一篇內容有繁中、簡中與英文三個版本,會長這樣:
<link rel="alternate" hreflang="zh-Hant" href="https://example.com/zh/about/" />
<link rel="alternate" hreflang="zh-Hans" href="https://example.com/cn/about/" />
<link rel="alternate" hreflang="en" href="https://example.com/en/about/" />
<link rel="alternate" hreflang="x-default" href="https://example.com/en/about/" />
拆開來看,rel="alternate" 表示這個 URL 是某頁的替代版本;hreflang="zh-Hant" 是語言(與選擇性的地區)代碼;href 是那個版本的完整網址。最後一行的 x-default 是給「語言或地區都對不上任何版本」的使用者一個預設落點,通常指向首頁或主要語言版本。
要記得的關鍵是:hreflang 本身不會強迫 Google 改變排名,它做的是消歧義。當你的內容有多個語言版本、彼此內容高度相似時,Google 原本可能把它們看成重複內容而只挑一個收錄,或是挑錯版本顯示給特定地區的人。hreflang 的作用,是讓 Google 知道這些 URL 是一組相互對應的翻譯,該把對的版本配給對的人,而不是讓它們互相稀釋。
為什麼少了 hreflang 會讓地區版本互相搶排名
地區版本互搶排名,本質上是搜尋引擎把你的多個語言版本當成「在競爭同一個查詢」的不同頁面,而不是「同一份內容的對應翻譯」。當 Google 不知道繁中頁和英文頁是同一篇東西的兩個版本,它會各自評估這兩頁的權重、各自決定要不要收錄、要不要在某個查詢下顯示。
結果有兩種,都不是你要的。第一種是 Google 把它們判定為重複內容,只索引其中一個版本,另一個版本直接被忽略,等於白做了一個語言。第二種是兩個版本同時被收錄,但在同一組關鍵字下彼此競食,搜尋引擎搖擺不定,最後兩頁的排名都比集中火力時更低。
對繁體中文站特別有感的是地區錯配。台灣使用者搜尋時,可能被丟出簡體中文版或英文版;反過來,你精心寫的繁中內容卻在台灣的搜尋結果裡輸給自己的其他語言版本。這在語言相近的繁中與簡中之間尤其常見,因為兩者文字高度重疊,Google 若沒有 hreflang 提示,很難自己判斷誰該配給台灣、誰該配給中國大陸。
hreflang 解決的就是這件事。它明確告訴搜尋引擎「這幾個 URL 是一組」,於是 Google 不再把它們當競爭對手,而是當成同一份內容的地區分身,依使用者語言與地區把流量導到對的版本,整組頁面的權重也能集中而非彼此抵銷。
最容易壞掉的 hreflang 寫法有哪些
hreflang 出名地難裝對,業界普遍觀察是大量網站的 hreflang 都帶著至少一個會讓 Google 直接忽略整組標註的錯誤。最致命的特性是:一組 hreflang 裡只要有一個環節壞掉,Google 往往會放棄整組標註,於是你以為裝了,實際上等於沒裝。下面這幾種是踩雷頻率最高的。
缺少回指標籤是最常見的致命錯誤
hreflang 是雙向互指的,這是它最反直覺、也最常出包的地方。如果英文頁的標註裡指向了繁中頁,那繁中頁的標註裡就必須也指回英文頁。只要其中一邊沒有指回去,這段對應關係就是單向的,Google 會直接忽略它。
換句話說,A 指向 B,B 也得指向 A,缺一不可。多語系站常見的破法是首頁設好了回指,但內頁、文章頁忘了在每一個語言版本都補上完整的對應,導致整站只有首頁的 hreflang 有效,其餘頁面全數失效。
每個版本都要有自我參照標籤
除了互指其他語言版本,每一頁還必須包含一個指向自己的 hreflang 標籤。也就是說,英文頁的標註裡,除了列出繁中版、簡中版,還要有一行指向英文版自己的 hreflang="en"。
漏掉自我參照,是另一個會讓 Google 忽略整組標註的高頻錯誤。正確的做法是:在每個語言版本上,都列出包含自己在內的完整語言清單。三個語言版本,每一頁就都要有三行 alternate(再加一行 x-default)。
語言與地區代碼要用對格式
hreflang 的語言代碼遵循 ISO 639-1,地區代碼遵循 ISO 3166-1 Alpha 2,兩者之間用連字號分隔,不能用底線。語言代碼可以單獨使用,但地區代碼不能單獨出現,一定要搭配語言代碼。
繁體與簡體中文的標法值得單獨拿出來講。中文的差異主要在書寫系統而非地區,所以建議用書寫系統子標籤:繁體中文寫 zh-Hant、簡體中文寫 zh-Hans。如果你的內容是針對特定地區量身做的(例如台灣的法規、用詞與簡體中文圈不同),也可以進一步用 zh-TW(台灣)與 zh-CN(中國大陸)來收窄。判斷原則很簡單:內容對所有同書寫系統的使用者都通用,就用 zh-Hant/zh-Hans;內容針對特定國家/地區有差異,才用到地區碼。
canonical 標籤與 hreflang 要互相對齊
canonical 標籤用來指出一頁的主要版本,很多 hreflang 災情其實出在 canonical 和 hreflang 打架。最典型的錯誤是:三個語言版本,每一頁的 canonical 都指回英文版。
這會讓 Google 困惑——hreflang 說這三頁是三個獨立的語言版本,canonical 卻說它們的主要版本都是英文頁。結果是繁中和簡中使用者可能被丟英文頁。正確做法是每個語言版本都用指向自己的自我參照 canonical:英文頁的 canonical 指向英文頁、繁中頁的 canonical 指向繁中頁,hreflang 的 href 也要與各頁的 canonical 完全一致。
URL 要用絕對網址且回傳 200
hreflang 的 href 必須是含 https:// 的完整絕對網址,不能用 /zh/page 這種相對路徑,否則搜尋引擎無法正確定位頁面。
同時,hreflang 裡列出的每一個 URL 都必須回傳 200 狀態碼。如果某個 URL 是 301/302 轉址、或是 404、500 錯誤,Google 會把它標為錯誤,連帶可能忽略整組標註。常見的破法是不小心把某個語言版本設了 noindex、或某頁已經刪除卻還留在 hreflang 清單裡。處理原則:只把可索引、可爬取、回傳 200 的頁面放進 hreflang,被擋爬或已失效的頁面就從清單移除。
不同版本不能指向同一個 URL
hreflang 靠的是不同語言版本對應到不同的 URL。如果你把兩個語言版本都指到同一個網址,Google 沒辦法知道該把哪個版本配給誰,這組標註就失去意義。反過來,同一個語言代碼也不該指向多個不同 URL。每個語言版本要對應到一個獨一無二的網址。
x-default 要設、且只設一次
x-default 是給「語言與地區都對不上任何版本」的使用者用的預設落點。少了它,這類使用者看到哪個版本就全憑 Google 自己決定,你失去控制權。設定上 x-default 通常指向首頁、語言選擇頁、或你的主要語言版本,而且整組標註裡只能出現一次,不要每個語言都標一個 x-default。
WordPress 怎麼設定 hreflang,三種外掛實作路徑
WordPress 上設定 hreflang 不必手寫,主流多語系外掛都會在你把翻譯彼此關聯起來後,自動產生 hreflang 標籤。重點不在於「會不會自動產」,而在於「有沒有把翻譯正確關聯、有沒有補上 x-default」。下面分外掛說明。
Polylang 的設定步驟與 x-default 補洞
Polylang 是免費版就能用的熱門選擇,會為所有已關聯的翻譯自動輸出 hreflang。設定流程是:先從外掛目錄安裝啟用 Polylang;接著到「語言 > 語言」把站台支援的每一種語言加進去,設定 locale、語言代碼與 URL 格式(建議用子目錄,例如 /zh/、/en/);然後為每一篇既有的文章與頁面指定語言;最後在編輯文章時,用側邊欄的「翻譯」欄位把它和其他語言的對應版本連結起來。關聯一旦建立,Polylang 就會在 <head> 自動輸出 hreflang。
要特別注意一個洞:Polylang 免費版在某些設定下不會自動補上 x-default。設定完成後,請到 Polylang 的設定確認 x-default 有指向你的主要語言;若使用 Polylang Pro,這部分會自動處理。
WPML 的設定步驟與 Yoast 接手機制
WPML 是使用最廣的付費多語系外掛,hreflang 全自動。流程是:安裝啟用後跑設定精靈,選好預設語言並加入所有支援語言;選擇 URL 結構(子目錄、子網域、或不同網域,一般以子目錄最易管理);接著逐篇翻譯內容,在每篇文章的 WPML 語言框點目標語言旁的「+」建立翻譯。WPML 會為每個有翻譯的頁面自動產生 hreflang,並補上指向預設語言的 x-default。
WPML 也會和 Yoast SEO、Rank Math 整合。當多語系外掛與 SEO 外掛同時啟用時,hreflang 的產生會交給 SEO 外掛接手,並多一層驗證。這層整合很有用,但也要知道它的存在,免得你以為是 WPML 在輸出、實際上是 Yoast 在管。
TranslatePress 的前端翻譯做法
TranslatePress 走的是另一條路,讓你直接在前台畫面上翻譯內容。安裝啟用後到設定加入語言,從管理工具列點「翻譯網站」開啟視覺化編輯器,逐一點選頁面上的文字元素就地翻譯。TranslatePress 會為所有已翻譯的頁面自動處理 hreflang。它把翻譯存在資料庫而不是另開文章,內容管理較單純,但 URL 結構的彈性相對受限。
Yoast SEO 與 Rank Math 接手後要檢查什麼
Yoast SEO 與 Rank Math 都會偵測常見的多語系外掛,並在兩者同時啟用時接手 hreflang 的產生,附帶三項驗證。第一是 canonical 對齊,SEO 外掛會確認 hreflang 的 href 與每頁的 canonical 一致,避免前面提過的 canonical 打架。第二是 noindex 處理,被標為 noindex 的頁面會自動排除在 hreflang 之外。第三是 sitemap 整合,hreflang 標註會被一併寫進 XML sitemap,等於給搜尋引擎第二個資料來源。
如果你用 Yoast SEO 搭配 WPML 或 Polylang,記得到 Yoast 的功能設定確認 hreflang 功能沒有被關掉;Rank Math 則不需額外設定就會自動處理。
自訂多語系架構要怎麼手動輸出 hreflang
如果你的多語系不是靠外掛,而是每個語言一套獨立 WordPress、或走 headless 架構,就得自己掛 hreflang。做法是在 wp_head 動作裡輸出 <link> 標籤,重點是把當前頁自己也加進清單(自我參照),並補上 x-default:
function add_hreflang_tags() {
if ( is_singular() ) {
$post_id = get_the_ID();
// 翻譯對應表:實務上從 post meta 或自訂資料表動態查出
$translations = array(
'zh-Hant' => get_post_meta( $post_id, '_url_zh', true ),
'zh-Hans' => get_post_meta( $post_id, '_url_cn', true ),
'en' => get_post_meta( $post_id, '_url_en', true ),
);
// 移除空值
$translations = array_filter( $translations );
// 把當前頁自己加進去(自我參照是必須的)
$current = substr( get_locale(), 0, 2 ) === 'en' ? 'en' : 'zh-Hant';
$translations[ $current ] = get_permalink();
foreach ( $translations as $lang => $url ) {
echo '<link rel="alternate" hreflang="' . esc_attr( $lang )
. '" href="' . esc_url( $url ) . '" />' . "n";
}
// x-default 通常指向主要語言版本
if ( ! empty( $translations['en'] ) ) {
echo '<link rel="alternate" hreflang="x-default" href="'
. esc_url( $translations['en'] ) . '" />' . "n";
}
}
}
add_action( 'wp_head', 'add_hreflang_tags' );
實際上線時,要把寫死的 URL 陣列換成從你的翻譯對應系統動態查出的結果,來源可以是 post meta、自訂資料表,或翻譯管理平台的 API。若是 WordPress Multisite、每個子站代表一種語言,則需要跨子站查詢,switch_to_blog() 函式能讓你在同一個網路內讀取另一個子站的資料、組出對應的翻譯網址。
頁面數量很多時,改用 XML sitemap 宣告 hreflang
頁面數量上千的網站,把 hreflang 寫進 XML sitemap 會比在每一頁的 HTML 都塞標籤更有效率。sitemap 做法把標註從頁面本體移走,減少每頁的標記量,也給搜尋引擎一個集中的 hreflang 資料來源。格式如下:
<url>
<loc>https://example.com/zh/about/</loc>
<xhtml:link rel="alternate" hreflang="zh-Hant"
href="https://example.com/zh/about/" />
<xhtml:link rel="alternate" hreflang="en"
href="https://example.com/en/about/" />
<xhtml:link rel="alternate" hreflang="x-default"
href="https://example.com/en/about/" />
</url>
WPML 與 Polylang 都會自動把 hreflang 標註加進各自的 sitemap;若搭配 Yoast SEO,Yoast 的 sitemap 也會包含 hreflang。HTML 標籤與 XML sitemap 兩種宣告可以同時用,Google 會各自讀取並自行調和,若兩者衝突,Google 會用自己的判斷規則決定對應關係。
語言數量很多(10 種以上)的站還要留意效能。每多一種語言,每一頁就多一個 <link>,15 種語言等於每頁多 16 行標記。減負的做法有三:把宣告移到 XML sitemap,讓頁面本體不帶這些標記;若是動態產生 hreflang,用 WordPress transient 或物件快取把結果快取起來,避免每次載入都重複查資料庫;至於 PDF 等非 HTML 資源,則改用 HTTP Link 標頭來宣告 hreflang。
上線後要怎麼驗證 hreflang 真的有生效
裝完 hreflang 不能憑感覺收工,因為一個小錯就能讓整組標註失效,驗證是必跑步驟。可以照下面這幾層由淺到深檢查。
第一層是檢視原始碼。打開每一個語言版本,在 <head> 裡搜 hreflang,逐項確認:標籤都在、有指回其他版本(雙向)、有指向自己(自我參照)、URL 都是含 https:// 的絕對網址、且彼此對應到不同網址。
第二層是 Google Search Console。在「國際指定」相關報告裡,Google 會直接列出缺少回指連結、未知語言代碼、hreflang 與 canonical 衝突等問題,這是最貼近 Google 實際解讀結果的訊號。
第三層是用爬蟲工具做全站檢查。頁數多的站靠肉眼看不完,Screaming Frog、Ahrefs 這類工具能一次掃完全站,揪出缺漏的 hreflang、非互指的單向連結、以及跨頁的語言代碼不一致。
驗證時最該盯緊的,永遠是前面點過的兩個致命錯誤:回指有沒有齊、自我參照有沒有漏。這兩項只要有一頁破了,那一組頁面的 hreflang 就會被 Google 整組丟掉,而它們偏偏又是最容易在內頁被忘記補的。把驗證排進每次新增語言、或大量改 URL 之後的固定流程,才能確保多語系站的權重集中在對的版本、不再彼此互搶。