3223 字
16 分鐘
【部落格架設】告別 WordPress,用 Astro 重建部落格的完整紀錄

【部落格架設】告別 WordPress,用 Astro 重建部落格的完整紀錄#

經營 WordPress 部落格這幾年,一直很在意網站速度和每個月的主機費用,直到發現 Astro 這個靜態網站框架,才決定把整個部落格搬家。這篇文章完整記錄從 WordPress 搬家到 Astro 的過程,包含文章轉移、圖片遷移到 CDN、部署到 Cloudflare Pages,以及搬家之後的心得。

為什麼要離開 WordPress#

說真的,WordPress 用了這麼多年其實並沒有什麼大問題,外掛豐富、SEO 工具完整、後台操作也直覺,但隨著文章愈積愈多,有幾個點開始讓我很不舒服:

1. 網站速度 — 每次用 PageSpeed Insights 測試分數都不好看,PHP 即時渲染加上一堆外掛,首次載入就花掉不少時間,Core Web Vitals 的分數一直是個心頭痛。

2. 主機費用 — 台灣的主機費用說貴不貴、說便宜也不算便宜,每個月固定支出,流量高的月份還要擔心超量,對個人部落格來說有點浪費。

3. 資安維護 — WordPress 核心、佈景主題、外掛三個面向都要持續更新,一不小心就有安全漏洞,雖然這幾年都沒出事,但心裡總是有一根刺。

Astro 是什麼?#

Astro 是一個專注於內容驅動網站的靜態網站框架,跟傳統框架最大的差別是它預設不會把 JavaScript 送到瀏覽器,也就是「零 JS」的概念。

靜態網站的意思是:每篇文章在 build 的時候就已經產生好 HTML 檔案,訪客進來直接拿到已經寫好的 HTML,不需要伺服器動態產生,速度自然比 WordPress 快很多。

搭配 Cloudflare Pages 免費方案部署的話,主機費用直接歸零,這對個人部落格來說真的很吸引人!!

選擇佈景主題#

Kent 最後選擇了 Fuwari 這個主題,外觀乾淨、支援深色/淺色模式、內建文章搜尋,對技術部落格來說功能已經很完整了。

主要使用的技術棧:

  • Astro 5.x — 靜態網站框架
  • Svelte 5.x — 互動元件
  • Tailwind CSS — 樣式
  • Pagefind — 全文搜尋(build 時自動建立索引)
  • Cloudflare Pages — 免費靜態網站託管
  • Cloudflare R2 — 圖片 CDN 儲存

文章遷移#

WordPress 原本有上百篇文章,一篇一篇複製貼上當然不是辦法,Kent 用 Node.js 寫了一套自動化腳本來處理,整體分成兩個步驟:

Step 1 — 用 WordPress REST API 拉取文章#

WordPress 內建 REST API,不需要任何外掛就能直接透過網址取得所有文章資料,路徑長這樣:

https://你的網域/wp-json/wp/v2/posts

抓取的思路如下:

  1. 分頁處理 — WordPress API 每次最多回傳 100 筆,超過 100 篇文章的話需要分頁。回應的 Header 裡有 X-WP-TotalPages 欄位,腳本會讀這個數字決定要跑幾頁。

  2. Retry 機制 — 網路偶爾會抖動,腳本加了 3 次自動重試,每次失敗等 1 秒再試,避免因為短暫的連線問題中斷整個抓取流程。

  3. 請求節流 — 每頁之間等 300ms,避免打太快被主機限流或封鎖。

  4. 一次抓齊 — 除了文章(posts)之外,媒體庫(media)、分類(categories)、標籤(tags)也一起抓下來,之後轉換時需要用到。

Terminal window
node fetch-all.js

執行完之後,data/ 目錄下會有 posts.jsonmedia.jsoncategories.jsontags.json 等檔案,這些就是後續轉換的原始資料。

Step 2 — 把 JSON 轉成 Markdown#

拿到 JSON 之後,再用 convert.js 把每篇文章轉成 Astro 可以讀取的 .md 格式。

轉換的思路:

  1. HTML → Markdown — 文章內容是 WordPress 存的 HTML,透過 turndown 這套工具轉成 Markdown,程式碼區塊、連結、圖片都能正確處理。

  2. 封面圖判斷 — 優先使用 WordPress 設定的精選圖片(featured_media),如果沒有就自動從文章內容抓第一張圖片當封面。媒體 ID 對應到實際網址則靠前一步抓下來的 media.json

  3. SEO description — 優先使用 WordPress 文章摘要(excerpt),若沒有摘要就從文章內容擷取前 160 個字,順便把 HTML 標籤清掉,避免摘要出現原始 HTML 語法。

  4. 分類和標籤 — WordPress 儲存的是 ID,需要對照 categories.jsontags.json 轉換成實際的名稱文字。

  5. Frontmatter 自動產生 — 標題、日期、slug、描述、封面圖、分類、標籤全部整合進 YAML frontmatter,轉出來就是 Astro 可以直接使用的格式。

Terminal window
node convert.js

轉換完的每篇文章都是一個 .md 檔案,直接放進 src/content/posts/ 就可以讓 Astro 讀取,整個流程跑完大約幾分鐘就能把幾百篇文章全部轉好,完全不需要手動處理。

圖片遷移到 Cloudflare R2#

原本 WordPress 的圖片都存在主機的 /wp-content/uploads/ 目錄,搬家之後這個路徑就失效了,所以決定把圖片都搬到 Cloudflare R2。

什麼是 R2?#

R2 是 Cloudflare 推出的物件儲存服務,定位跟 AWS S3 差不多,但有一個很重要的差別:流量費用(egress)免費。S3 每次有人讀取檔案都要收出流量費,R2 不收,對圖片這種高存取的靜態資源來說非常划算。免費額度每個月有 10GB 儲存空間,對個人部落格來說完全夠用!!

建立 R2 Bucket#

STEP 1

登入 Cloudflare Dashboard,左側選單找到《 R2 物件儲存 》,點擊《 建立儲存貯體 》,輸入 Bucket 名稱(Kent 用的是 blog3c-assets)。

STEP 2

建立完成後,進入 Bucket 設定頁面,找到《 自訂網域 》,綁定自己的網域,例如 img.blog3c.net。綁定之後 Cloudflare 會自動幫你把這個網域對應到 R2,外部存取就直接用這個網域即可。

STEP 3

在《 R2 API 》頁面建立 API Token,權限選《 物件讀取 & 寫入 》,記下 Access Key IDSecret Access Key,之後設定 rclone 時需要用到。

用 rclone 同步圖片#

rclone 是一套支援各種雲端儲存的命令列工具,R2 支援 S3 相容 API,所以 rclone 設定時選 S3 類型並填入 Cloudflare 的端點即可:

[r2]
type = s3
provider = Cloudflare
access_key_id = 你的Access Key ID
secret_access_key = 你的Secret Access Key
endpoint = https://<帳號ID>.r2.cloudflarestorage.com

設定完成後,一行指令把本機的 images/ 目錄同步到 R2:

Terminal window
bash sync-images-to-r2.sh

同步完成後,再把所有 Markdown 文章裡的圖片路徑批次替換成 CDN 網址:

https://img.blog3c.net/圖片名稱.png

部署到 Cloudflare Pages#

整個部落格最終是部署在 Cloudflare Pages 上,不是傳統的虛擬主機或 VPS,而是 Cloudflare 提供的靜態網站託管服務。每次 build 完把 dist/ 目錄推上去,Cloudflare 就會自動幫你分發到全球的 CDN 節點,訪客就近取得內容,速度非常快。免費方案對個人部落格已經完全足夠,不需要另外付主機費。

部署工具使用的是 Wrangler CLI,設定好 wrangler.toml 之後,一行指令就能 build 並部署:

Terminal window
npm run deploy

這個指令背後做的事情:

  1. astro build — 產生靜態 HTML/CSS/JS
  2. pagefind --site dist — 建立搜尋索引
  3. wrangler pages deploy dist --branch master — 部署到 Cloudflare Pages 正式環境
  4. 呼叫 Cloudflare API 自動清除 CDN 快取

Google Analytics、AdSense 與 SEO 設定#

網站架好之後,流量追蹤和廣告收益的設定當然也不能少,這部分在 Astro 上的做法跟 WordPress 差很多,WordPress 有外掛一鍵搞定,Astro 需要自己動手寫進 HTML。

Google Analytics#

GA4 的追蹤碼直接寫進 Layout.astro(所有頁面共用的版面骨架),為了不影響網站效能,Kent 選擇搭配 Partytown 讓 GA 在 Web Worker 裡執行,不佔用主執行緒:

<script type="text/partytown"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX">
</script>
<script type="text/partytown">
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXXXXX');
</script>

跟一般直接貼上 <script async> 的差別是,Partytown 會把這段腳本移交給 Web Worker 處理,讓 GA 不會拖慢頁面渲染速度,對 Core Web Vitals 有幫助。

Google AdSense#

AdSense 設定上遇到的第一個問題是廣告版位的規劃。WordPress 上用的是 Ad Inserter 外掛,可以設定多個廣告 Block 插入不同位置。搬到 Astro 之後需要自己決定版位,Kent 最後設定了 5 個版位:

  • 文章內廣告 ×2(In-article fluid)— 插在文章內文結尾
  • Autorelaxed 廣告 — 文章底部
  • Multiplex 廣告 — 文章底部
  • 側欄廣告(auto 尺寸)— 桌機版側欄

AdSense 腳本一樣放在 Layout.astro<head> 裡:

<script async
src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-XXXXXXXXXXXXXXXX"
crossorigin="anonymous">
</script>

另外要記得在網站根目錄放 ads.txt,內容格式如下,沒有這個檔案 AdSense 審核會過不了:

google.com, pub-XXXXXXXXXXXXXXXX, DIRECT, f08c47fec0942fa0

SEO Meta Tags#

WordPress 的 SEO 靠 Rank Math 或 Yoast 外掛處理,搬到 Astro 之後這些都要自己補上。Kent 在 Layout.astro 裡加了以下這些 meta tag:

<!-- Open Graph(FB、LINE 分享預覽) -->
<meta property="og:title" content="文章標題" />
<meta property="og:description" content="文章摘要" />
<meta property="og:image" content="封面圖網址" />
<meta property="og:type" content="article" />
<meta property="og:locale" content="zh_TW" />
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content="封面圖網址" />
<!-- 文章專用(發布時間、修改時間、作者) -->
<meta property="article:published_time" content="ISO日期" />
<meta property="article:modified_time" content="ISO日期" />
<meta property="article:author" content="Kent" />
<!-- Canonical(避免重複內容) -->
<link rel="canonical" href="文章網址" />

文章封面圖的 og:image 直接從文章的 frontmatter image 欄位取得,這樣每篇文章分享出去都會有對應的封面圖預覽,不會全部都顯示同一張。

Google Search Console#

Search Console 的驗證方式選《 HTML 標籤驗證 》,把驗證碼加進 <head> 即可:

<meta name="google-site-verification" content="驗證碼" />

驗證完成後,把 sitemap 網址提交給 Search Console:

https://blog3c.net/sitemap-index.xml

Astro 有 @astrojs/sitemap 套件,build 的時候會自動產生 sitemap,不需要另外設定。

搬家之後遇到的坑#

老實說搬家的過程不是一帆風順,這裡記錄幾個比較印象深刻的問題:

Wrangler 部署後只顯示「Cloudflare Pages not deploying」 — 這個坑卡了蠻久的!!安裝好 Wrangler 之後,第一次執行 wrangler login 進行 Cloudflare 驗證,過程中 Wrangler 會在 Cloudflare Pages 後台幫你建立一個預設的專案和分支。問題是預設分支不一定是 master,如果在驗證或初始化的過程中改動了分支名稱,或是用錯分支部署,Cloudflare Pages 後台的「正式環境」分支跟你實際推的分支對不上,訪客就只會看到「Cloudflare Pages not deploying」的提示,但 build log 看起來完全正常,非常難 debug。

解法是在 npm run deploy 的 wrangler 指令明確加上 --branch master

Terminal window
wrangler pages deploy dist --branch master

這樣就能強制指定部署到正式環境分支,不會再出現頁面空白的問題。

URL 轉址無限迴圈 — 舊網站的文章 URL 是 /blog/slug,新網站是 /blog/post/slug,設定 _redirects 轉址規則的時候用了萬用字元 /blog/*,結果 /blog/post/slug 也被匹配到,一直跳轉造成無限迴圈。解法是改用具名參數 /blog/:slug 只匹配單層路徑。

Tailwind 跨檔案 @apply 失效 — 在 markdown.css 裡用 @apply link 引用了定義在 main.css 的 class,在某些環境下會 build 失敗。解法是把樣式直接寫進 CSS,不要跨檔案 @apply

WordPress HTML 實體字元殘留 — 文章標題裡有大量 &#8211;(en dash)、&#8217;(右單引號)等 HTML 實體,轉換腳本沒有處理到,需要用 sed 批次替換。

搬家之後的感受#

部落格搬到 Astro 之後,第一個明顯感受是網站真的快很多,靜態 HTML 加上 Cloudflare CDN,全球載入速度都很穩定。

費用方面,Cloudflare Pages 和 R2 的免費額度對個人部落格完全夠用,等於主機費直接省下來了。

搜尋功能用 Pagefind 實作,build 的時候會自動對所有文章建立索引,不需要額外的伺服器,在靜態網站上就能有完整的全文搜尋,這點讓我很滿意。

唯一的缺點是技術門檻相對高,如果不熟悉前端工具鏈,環境設定和 debug 可能會花不少時間,不像 WordPress 安裝完就能馬上用。不過對工程師來說,這些反而是有趣的地方~

結論#

如果你也是個人部落客,每個月在付主機費用、又在意網站速度,Astro + Cloudflare Pages 這個組合非常值得考慮。尤其是文章數量多的部落格,靜態化之後的速度提升會非常明顯!!

當然每個人的情況不一樣,WordPress 的生態系和外掛豐富度仍然無可取代,搬家之前最好先評估看看你最在乎的是什麼,再決定要不要動~

延伸閱讀#

👉 【雲端主機】vCenter雲端主機介紹 v2ray VPN架設教學
👉 【NAS DIY】舊電腦先別丟!! 安裝 TrueNAS 15分鐘打造高效能網路硬碟
👉 【NAS DIY】5分鐘添加TrueCharts軟體庫,讓TrueNAS有更多內建APP

【部落格架設】告別 WordPress,用 Astro 重建部落格的完整紀錄
https://blog3c.net/blog/post/wordpress-to-astro-migration/
作者
Kent
發佈於
2026-04-28
許可協議
CC BY-NC-SA 4.0