結論先講

網站搬家沒做 redirect = SEO 自殺。 我自己就犯了這個錯:300 篇文章從 Hexo 搬到 Quartz,URL 結構全變,沒做任何 redirect。結果 Google 索引的全是 404 的舊頁面,新文章一篇都搜不到。

這篇不是教科書,是驗屍報告。所有的「正確做法」都是我踩完坑之後才學到的。


我的搬家時間線

2024-09  Hexo 部落格正常運作,Google 收錄約 30 頁
         URL 格式:/{category}/{YYYYMMDD}/{abbrlink}/
         例:/devOps/20240915/3152381087/

2025-02  搬家到 Quartz,重新設計 URL 結構
         URL 格式:/{category}/{slug}
         例:/git/08-release

2025-02~ Google 還在索引舊 URL,全部 404
2026-03  新 URL 一篇都沒被收錄
         搜 "site:terryyaowork.github.io" 只有 6 筆結果
         全部都是舊的 404 頁面 💀

一年多的時間,300 篇新文章對 Google 來說完全不存在。


為什麼會這樣?

URL 結構變了但沒告訴 Google

Hexo 用 abbrlink 產生數字 hash 當 URL:

/devOps/20240915/3152381087/  ← Hexo (abbrlink)
/git/08-release               ← Quartz (slug)

兩個 URL 完全不同。Google 不會自動知道它們是同一篇文章。

Google 的行為邏輯

1. Google 資料庫裡有舊 URL
2. Googlebot 去爬 → 404
3. Google 不會立刻刪除索引,會重試幾次
4. 持續 404 → 最終降權或移除
5. 但新 URL 沒有任何入口 → Google 不知道它存在
6. 即使有 sitemap,Google 也不信任一個「舊頁面都壞掉」的網站

結果:舊的被移除或降權,新的沒被發現。兩頭空。


正確的搬家 SEO 流程

Phase 1:搬家前(準備)

1. 記錄舊 URL 清單

搬家前最重要的事:把所有舊 URL 存下來。

# 從舊站的 sitemap 匯出
curl -s https://your-old-site.com/sitemap.xml | grep "<loc>" > old-urls.txt
 
# 或從 Google Search Console 匯出
# GSC → 成效 → 匯出 → 所有頁面

如果你跟我一樣沒存… 只能從 Google 搜尋結果反推了:

# 搜尋 Google 索引中的舊頁面
# site:your-site.com inurl:2024

2. 建立舊→新 URL 對照表

舊 URL                              → 新 URL
/devOps/20240915/3152381087/         → /git/08-release
/devOps/20240915/2194877756/         → /git/05-git-flow
/express/web-develop/20241017/368853435/ → /express/01-init

如果文章很多,用腳本比對標題自動配對:

// match-urls.mjs
// 從舊 URL 的標題比對新文章
import { readdirSync, readFileSync } from 'fs';
 
const OLD_URLS = [
  { url: '/devOps/20240915/3152381087/', title: 'Release 管理' },
  // ...
];
 
// 掃描新文章的 title
function findNewUrl(oldTitle) {
  // 遞迴搜尋 content/ 底下所有 .md
  // 比對 frontmatter title 包含 oldTitle
  // 回傳新的 URL path
}

3. 在 GSC 確認目前索引狀態

GSC → 索引 → 涵蓋範圍
- 看有多少頁被索引
- 記錄下來,搬家後比較

Phase 2:搬家時(執行)

4. 建立 Redirect

最佳方案:伺服器端 301 redirect

如果你用 Nginx / Apache:

# nginx.conf
location ~ ^/devOps/20240915/3152381087/ {
    return 301 /git/08-release;
}

GitHub Pages 方案:HTML meta refresh

GitHub Pages 不支援伺服器端 redirect。替代方案是在舊路徑放 HTML 檔案:

<!-- public/devOps/20240915/3152381087/index.html -->
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <link rel="canonical" href="https://site.com/git/08-release">
  <meta http-equiv="refresh" content="0; url=https://site.com/git/08-release">
  <meta name="robots" content="noindex">
  <script>window.location.replace("https://site.com/git/08-release");</script>
</head>
<body>
  <p>已搬移至 <a href="/git/08-release">新頁面</a></p>
</body>
</html>

三重保險:

  1. canonical — 告訴 Google 正版 URL
  2. meta refresh — 瀏覽器自動跳轉
  3. noindex — 不要索引這個 redirect 頁面

5. 智慧 404 頁面

處理已知的 redirect 之後,還要處理未知的舊 URL。在 404 頁面加 JS,偵測舊 URL 格式並導向:

// 注入到 404.html
(function() {
  var path = window.location.pathname;
 
  // 已知的 redirect
  var map = {
    '/devOps/20240915/3152381087/': '/git/08-release',
    '/devOps/20240915/2194877756/': '/git/05-git-flow',
    // ...
  };
  if (map[path]) {
    window.location.replace(map[path]);
    return;
  }
 
  // 通用規則:/{category}/{date}/{hash}/ → /{category}/
  var match = path.match(/^\/([a-zA-Z-]+)\/\d{8}\/\d+\//);
  if (match) {
    var catMap = {
      'devOps': '/devOps/',
      'git': '/git/',
      'express': '/express/',
      'aws': '/aws/'
    };
    window.location.replace(catMap[match[1]] || '/');
  }
})();

6. 更新 Sitemap

確保新的 sitemap.xml 包含所有新 URL,不包含舊 URL。

<!-- sitemap.xml -->
<url>
  <loc>https://site.com/git/08-release</loc>
  <lastmod>2025-02-09</lastmod>
</url>
<!-- 不要包含舊的 /devOps/20240915/... -->

Phase 3:搬家後(驗證)

7. 到 GSC 提交新 Sitemap

GSC → Sitemap → 新增 Sitemap
輸入:sitemap.xml → 提交

8. 用「網址審查」要求重新索引

對最重要的幾篇文章,手動要求 Google 重新爬取:

GSC → 網址審查 → 輸入新 URL
→ 要求建立索引

每天有配額限制(約 10-50 次),先提交最重要的頁面。

9. 監控索引狀態

每週檢查:
- GSC 涵蓋範圍 → 已索引頁面數是否增加
- Google 搜尋 site:your-site.com → 結果數是否增加
- 舊 URL 是否逐漸從索引中消失

完整恢復通常需要 2-8 週,取決於網站規模和 Google 爬取頻率。


自動化:CI/CD 整合 Redirect

如果用 GitHub Actions 部署,把 redirect 建立整合進 build 流程:

# .github/workflows/deploy.yml
- name: Build Quartz
  run: node quartz/bootstrap-cli.mjs build
 
- name: Setup redirects for old URLs
  run: node scripts/setup-redirects.mjs
  # 這個腳本:
  # 1. 在 public/ 建立舊路徑的 redirect HTML
  # 2. 注入 redirect JS 到 404.html
 
- name: Upload artifact
  uses: actions/upload-pages-artifact@v3

這樣每次部署都會自動建立 redirect,不用擔心手動維護。


搬家 SEO 檢查清單

搬家前

  • 匯出舊 URL 完整清單
  • 建立舊→新 URL 對照表
  • 記錄 GSC 目前索引數量
  • 備份舊站(以防萬一)

搬家時

  • 所有舊 URL 有 redirect 到新 URL
  • Redirect 包含 canonical + noindex
  • 404 頁面有智慧 redirect 邏輯
  • 新 sitemap 只包含新 URL
  • robots.txt 正確(Allow + Sitemap)

搬家後

  • GSC 提交新 sitemap
  • GSC 網址審查重點頁面
  • 每週監控索引恢復狀態
  • 確認舊 URL 被正確 redirect(不是 404)

我的教訓總結

我做錯的正確做法
沒存舊 URL 清單搬家前先匯出
沒做 redirect每個舊 URL 都要 redirect
沒提交新 sitemap搬完立刻提交
等了一年才發現搬完一週內就該檢查 GSC
以為 Google 會自己找到Google 不會,要你告訴它

最痛的教訓:搬家不是搬完就結束,SEO 的處理才是搬家最重要的一步。 寫了 300 篇文章,但 Google 看不到,等於沒寫。

本系列文章

  1. SEO 基礎與注意事項
  2. 技術 SEO 實作
  3. 內容 SEO 策略
  4. 網站搬家 SEO(本篇)
  5. GSC 實戰指南
  6. Open Graph 與社群分享
  7. AEO 基礎:AI 搜尋引擎怎麼找答案
  8. AEO 內容策略
  9. SEO vs AEO 整合策略
  10. Core Web Vitals 效能優化
  11. AEO 監控自動化
  12. 案例:從 0 到被搜到