【GitHub、Hexo】jsDelivr CDN 加速 GitHub Pages 資源及 Hexo 實作

【GitHub、Hexo】jsDelivr CDN 加速 GitHub Pages 資源及 Hexo 實作

本篇重點

  • 使用 jsDelivr CDN 串接 GitHub Pages 資源,延長快取時長、加速載入
  • 了解 GitHub Pages 與 jsDelivr 在快取、全球節點、資源更新速度上的差異
  • 了解 jsDelivr CDN 串接原理與版本控制
  • 使用 Hexo 過濾器實現本地資源重寫
  • 使用自動化佈署腳本與 CDN Tag 機制實現佈署後強制更新快取

jsDelivr CDN

一個免費、開源的公共 CDN,專門用於託管 GitHub 倉庫、npm 包等資源,並提供全球多個節點 + 彈性快取 + fallback 機制。

健忘筆記

CDN(內容傳遞網路,Content Delivery Network)是一種分散式伺服器網路系統,其核心目的是加速網站內容的傳輸速度,讓全球各地的使用者都能快速存取網站內容。

串接 GitHub Pages 的運作方式

支援透過特定的 URL 結構來直接存取 GitHub 倉庫中的文件。

URL 結構:

jsdelivr cdn
1
2
3
4
5
6
7
8
9
https://cdn.jsdelivr.net/gh/使用者名稱/倉庫名稱@版本號/文件路徑

// 範例
https://cdn.jsdelivr.net/gh/forgetfulengineer/forgetfulengineer.github.io@v-20251208-172935/gallery/covers/Handling-Case-Sensitivity-Issues-in-GitHub-Deployment.webp

https://cdn.jsdelivr.net/gh/forgetfulengineer/forgetfulengineer.github.io@lateset/gallery/covers/Handling-Case-Sensitivity-Issues-in-GitHub-Deployment.webp

// 無使用版本號,會自動存取主分支的資源,例如:@main
https://cdn.jsdelivr.net/gh/forgetfulengineer/forgetfulengineer.github.io/gallery/covers/Handling-Case-Sensitivity-Issues-in-GitHub-Deployment.webp

@版本號 參數可為 Git 分支名Commit SHATag 名稱。此參數用於鎖定特定版本的資源,是實現 CDN 快取強制更新(Cache Busting)的核心機制。

特性:

  • 從 GitHub API / 原始檔案擷取資料。
  • 調用的是 GitHub 資源,但由 jsDelivr 分發,避免 GitHub 本機延遲。
  • 若使用 tag 或 commit hash,jsDelivr 視為不可變版本,會使用長效快取(例如 max-age 1 年)。
  • 若使用分支(如 mainlatest),屬於「可變內容」,會使用短效快取(例如 max-age 600 秒)。
  • 整合多家 CDN,若某一節點故障,自動 fallback 至其他節點,提升可用性與抗故障能力。
  • 適合用於靜態資源 (CSS, JS, 圖片),利用 Multi-CDN 優勢最大化下載速度。

GitHub Pages 資源 vs jsDelivr CDN

GitHub PagesjsDelivr CDN
CDN 架構單一主要供應商 (Fastly)。Multi-CDN (多重 CDN 聚合)。整合了 Cloudflare、Fastly、Bunny 等多個供應商。
節點覆蓋Fastly POP(edge location)服務,依訪問者地理位置優化。整合多家 CDN(Cloudflare、Fastly、Bunny 等),提高緩存命中率與可用性。
快取政策快取策略較短(例如 max-age 600秒)。依 CDN 版本號決定快取策略,支援 immutable tag 機制對應長效快取(例如 max-age 1 年),對開源專案資源尤其友好。
版本更新靜態內容更新後由預設機制重新佈署與快取刷新。透過更新資源 URL(如新的 tag)實現版本鎖定與 cache busting。
故障轉移無。支援自動 Failover(Cloudflare + Fastly + Bunny 等)提供高穩定性。
適用場景網站主體 HTML 文件、動態更新頻繁的內容。靜態資源(圖片、CSS、JS)。利用 Multi-CDN 優勢最大化下載速度。

Hexo 實作方式

Hexo 實作流程

目標是透過 Hexo 過濾器(Filter)在網站生成時,將 HTML 文件中指向本地靜態資源的相對路徑替換為 jsdelivr CDN 的絕對路徑。

過濾器重寫資源 URL

新增腳本(rewrite_local_asset.js,以下稱 ‘重寫腳本’)放在 scripts 資料夾下,讓 Hexo 初始化時自動執行。

rewrite_local_asset.js >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

/**
* 本地資源重寫腳本
* 使用 Cheerio 解析 HTML,將 / 開頭的路徑改寫為 jsdelivr CDN prefix
*/

'use strict';

const cheerio = require('cheerio');

const enable = true;
let prefix = 'https://cdn.jsdelivr.net/gh/使用者名稱/倉庫名稱';

hexo.extend.filter.register('after_render:html', function (html) {
// 本地開發模式不重寫
if (['s', 'server'].includes(hexo.env.cmd)) return html;

if (!enable || !prefix) return html;

prefix = prefix.replace(/\/+$/, '');

if (process.env.CDN_TAG) {
prefix = prefix.includes('@')
? prefix.replace(/@([^\/]+)/, `@${process.env.CDN_TAG}`)
: `${prefix}@${process.env.CDN_TAG}`;
}

if (!prefix) return html;

const $ = cheerio.load(html, { decodeEntities: false });

/**
* 通用重寫與 Fallback 邏輯
* @param {object} el - Cheerio 元素物件
* @param {string} attrName - 屬性名稱 (src 或 href)
*/
const rewriteAndSetFallback = (el, attrName) => {
const $el = $(el);
const originalUrl = $el.attr(attrName);

// 如果 URL 為空或不是以 / 開頭的 URL均不處理
if (!originalUrl || !originalUrl.startsWith('/')) return;

// 將本地路徑轉換為 CDN 路徑
const cdnUrl = prefix + originalUrl;

// Fallback 設置
// 當 CDN 載入失敗時(onerror handler),將 src/href 切換回本地資源
$el.attr('onerror', `this.onerror=null;this.setAttribute('${attrName}', '${originalUrl}');`);

// 設置重寫後的 CDN URL
$el.attr(attrName, cdnUrl);
};

$('img').each((i, el) => rewriteAndSetFallback(el, 'src'));
$('script').each((i, el) => rewriteAndSetFallback(el, 'src'));
$('link').each((i, el) => {
const $el = $(el);
const rel = $el.attr('rel');

// 排除 Favicon、Icons、Sitemap、Alternate 等特定 rel 屬性
if (rel && /icon|apple-touch-icon|sitemap|manifest|alternate/i.test(rel)) {
return;
}

rewriteAndSetFallback(el, 'href');
});

return $.html();
});

說明

  • 重寫腳本註冊 after_render:html 過濾器,在 Hexo 將 Markdown 轉換為 HTML 之後執行。

  • 重寫腳本解析生成的 HTML(使用 Cheerio),針對所有以 / 開頭 (root-relative) 的資源 (例如 <link href="/css/style.css">) 進行 rewrite。

    1. 注入 CDN Tag: 腳本首先檢查環境變數 process.env.CDN_TAG。如果存在,它會將這個 Tag 名稱(例如 v-20251208-140000)注入到 CDN 的 URL Prefix 中,實現版本控制。
    2. CDN URL 生成: 對於符合條件的本地路徑 originalUrl,新的 CDN URL cdnUrl 會被構建為 prefix + originalUrl

    範例:
    如果 prefixhttps://cdn.jsdelivr.net/gh/user/repo@v-latestoriginalUrl/images/logo.png,那 cdnUrl 會是 https://cdn.jsdelivr.net/gh/user/repo@v-latest/images/logo.png

  • 避免 CDN 故障 (cdn 回傳 404 或 downtime),對所有被 rewrite 的元素加上 onerror fallback:

    1
    $el.attr('onerror', `this.onerror=null;this.setAttribute('${attrName}', '${originalUrl}');`);
    • this.onerror=null 防止在回退到原始路徑後若仍失敗導致無限循環
    • 將元素的 srchref 屬性切換回原始的本地相對路徑 (originalUrl),確保網站的可用性。
  • 排除特定 rel 屬性的 <link> 元素是為了減少 SEO快取策略以及跨來源安全限制(CORS)的問題。

自動化佈署腳本與 CDN 版本控制

新增腳本(deploy.sh,以下稱 ‘佈署腳本’)放在 Hexo 專案資料夾下,負責網站的清理、生成、佈署,以及最重要的 CDN 快取強制更新機制。

rewrite_local_asset.js >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78

#!/bin/bash

# GitHub Pages repository 的 URL
REPO_URL="https://github.com/使用者名稱/倉庫名稱"
USER_NAME="使用者名稱"
USER_EMAIL="使用者信箱"
FIRST_DEPLOY=0

# 產生本次佈署的 Tag 名稱 (格式: v-YYYYMMDD-HHMMSS)
CDN_TAG="v-$(date +'%Y%m%d-%H%M%S')"

# 檢查 .deploy_git 資料夾是否存在
echo -e "\n檢查 .deploy_git 資料夾是否存在..."
if [ -d ".deploy_git" ]; then
# 切換到 .deploy_git 資料夾並拉取最新進度
cd .deploy_git || { echo -e "\n切換到 .deploy_git 資料夾失敗"; exit 1; }
echo -e "\n拉取 github page 最新進度..."
git pull || { echo -e "\n拉取進度失敗"; exit 1; }
cd .. || { echo -e "\n返回 Hexo 資料夾失敗"; exit 1; }
else
# 檢查 GitHub Pages repository 是否已有 commit
echo -e "\n檢查 GitHub Pages repository 是否已有 commit..."
if git ls-remote --exit-code "$REPO_URL" HEAD > /dev/null 2>&1; then
echo -e "\nClone repository 到 .deploy_git 資料夾..."
git clone "$REPO_URL" .deploy_git || { echo -e "\nClone repository 失敗"; exit 1; }

# 設置 git config
cd .deploy_git || { echo -e "\n切換到 .deploy_git 資料夾失敗"; exit 1; }
git config --local user.name "$USER_NAME"
git config --local user.email "$USER_EMAIL"
git config --local core.ignorecase false
cd .. || { echo -e "\n返回 Hexo 資料夾失敗"; exit 1; }
else
echo -e "\nGitHub Pages repository 尚無 commit,進行首次佈署"
FIRST_DEPLOY=1
fi
fi

# 清理舊的生成文件
echo -e "\n清理舊的生成文件..."
hexo clean || { echo -e "\nHexo 清理失敗"; exit 1; }

# 將 CDN_TAG 注入環境變數,讓 rewrite_local_asset.js 讀取
export CDN_TAG="$CDN_TAG"

# 生成新的靜態文件並佈署網站
echo -e "\n生成新的靜態文件並佈署網站..."
hexo g || { echo -e "\nHexo 生成失敗"; exit 1; }
hexo d || { echo -e "\nHexo 佈署失敗"; exit 1; }

# 對佈署的 Commit 打 Tag
echo -e "\n正在對 GitHub Pages 建立 Tag: ${CDN_TAG}..."

cd .deploy_git || { echo -e "\n無法進入 .deploy_git,Tag 建立失敗"; exit 1; }

# 確保在最新的 commit 上
LATEST_COMMIT=$(git rev-parse HEAD)
echo -e "\n最新 Commit: ${LATEST_COMMIT}"

# 建立 Tag
git tag -a "$CDN_TAG" $LATEST_COMMIT -m "Release: $LATEST_COMMIT"

# 推送 Tag 到遠端
echo -e "\n推送 Tag 到 GitHub..."
git push origin "$CDN_TAG"
cd ..

# 如果是首次佈署,進入 .deploy_git 設置 git config
if [ "$FIRST_DEPLOY" -eq 1 ]; then
echo -e "\n首次佈署,設置 git config --local core.ignorecase false"
cd .deploy_git || { echo -e "\n切換到 .deploy_git 資料夾失敗"; exit 1; }
git config --local core.ignorecase false
cd .. || { echo -e "\n返回 Hexo 資料夾失敗"; exit 1; }
fi

echo -e "\n佈署完成"

說明

  • 產生唯一的 CDN_TAG腳本在佈署開始時,自動生成唯一且代表當前版本的 CDN_TAG,格式為 v-YYYYMMDD-HHMMSS
  • 注入環境變數:export CDN_TAG="$CDN_TAG" 將 Tag 注入到環境變數中,供重寫腳本讀取並整合進 CDN URL。
  • 將該 CDN_TAG 設為環境變數,供重寫腳本使用,以產生對應版本的 CDN URL。
  • 生成並佈署網站:hexo g (Generate) 執行時,重寫腳本會使用最新的 CDN_TAG 重寫所有本地資源路徑。
  • 建立並推送 Git Tag:在 Hexo 成功佈署,將靜態文件推送到 GitHub Pages 倉庫後,腳本在 .deploy_git 資料夾內,對最新的 Commit 建立並推送該 CDN_TAG 到遠端 GitHub 倉庫。
  • CDN URL 隨版本改變(tag 不同),實現「Cache Busting」策略:舊版資源仍可被 cache,但新版資源 URL 不同,使用者會重新拉取最新資源,實現強制更新,避免被舊資源覆蓋或快取污染。

結論

雖然 GitHub Pages 本身已透過 Fastly 提供優秀的 CDN 服務,但透過 jsdelivr 的 Multi-CDN 架構與 Hexo 自動化腳本的整合,可以進一步優化靜態資源的交付策略。

此方案的核心價值在於「資源版本控制」:利用自動化的 Git Tag 機制,確保每次佈署的資源都擁有唯一的 URL,解決了靜態資源的快取更新問題,更讓網站能享受到 jsdelivr 針對資源提供的長效快取,同時配合 onerror 回退機制確保了服務的高可用性。

延伸閱讀

【GitHub、Hexo】jsDelivr CDN 加速 GitHub Pages 資源及 Hexo 實作

https://forgetfulengineer.github.io/Other/GitHub/jsdelivr-cdn-github-pages-hexo-acceleration-tutorial/

作者

健忘工程師

發表於

2025-12-21

更新於

2025-12-21

許可協議


你可能也想看

【HTML】解析 <a> 的 rel 屬性
【Tool】Unix 時間戳(Unix timestamp)轉換
【Hexo】Icarus 主題串接 GA 顯示網站 PV 和 UV

評論

複製完成