前言 #
前一篇有提到我把部落格從github遷移到cloudflare,詳見「部落格從 GitHub Pages 遷移:為什麼我選擇 Hugo 搭配 Cloudflare Pages」,這篇就來記錄一下我如何購買個人網域、設定好,並把圖片自動上傳到cloudflare r2的流程。
購買 Cloudflare 網域 #
為什麼會選擇在 cloudflare 購買網域呢?主要有以下幾個原因:
- 價格實惠:幾乎是以成本價提供。
- 續約價格透明:不用擔心未來被養套殺,續約價格是固定的。
- 整合度高且安全:內建許多實用的免費功能,能看網域流量分析,還有一些基本的資安防護。
- 全球 CDN 加速:自帶強大的全球 CDN 節點,能大幅提升網站與圖片的載入效率。
實際的購買與註冊流程如下:
1. 搜尋並註冊網域 #
首先,前往 Cloudflare dashboard 頁面,在左側選單找到 網域 -> 購買網域。
接著輸入你想要的網域名稱,下方會列出可用的後綴(如 .com、.dev、.io 等)與價格,我選了最常見的.com 結尾的網域,價格1年10美金,也算是很漂亮的價格。

然後就是選你要幾年的方案,我是直接先買了3年,也可以先買1年後面再自動續約,挑選好後,輸入個人資料,完成結帳付款即可。

2. 設定 DNS 與網域管理 #
購買完成後,該網域會自動加入到你 Cloudflare 的帳號中。你可以到左側的 網域 點擊查看剛剛購買的網域。

然後進入概觀頁面,首次進來會要你設定 DNS 與 憑證,到這裡你就可以透過剛剛購買的網域來存取你的網站了!
接下來只要按照這篇第六步-管理自訂網域設定就能把部署到cloudflare的網站指向購買的網域即可。
此外,你也可以把在其他地方部署的網站,照著網頁上的說明設定將 ip 指向自訂網域上。

設定完成後,之後再點進概觀頁面,就會看到cloudflare的流量分析了,也可以到分析與紀錄看更詳細的紀錄。

架設 Cloudflare R2 圖床 #
既然網域買好了,接下來就是設立圖床空間啦!而 Cloudflare R2 本身是一個與 AWS S3 兼容的物件儲存服務
,使用者可以使用現有工具上傳檔案,而且免費額度對於一般的個人用部落格來說,已經非常夠用了(每月 10 GB 儲存與數百萬次讀寫)。

詳細額度說明可以參考:Cloudflare R2 收費標準
1. 新增 R2 貯體 (bucket) #
在 Cloudflare 後台左側選單找到 R2-> 概觀,要開始使用需要先綁定一張信用卡。

接著點擊右上角的 建立貯體,輸入貯體名稱(例如 cdn),並選擇貯體的儲存位置(預設為 亞太地區)。

2. 開放公開存取與綁定自訂網域 #
預設情況下,R2 儲存區是私有的。為了讓網頁能讀取圖片,我們需要允許公開存取。
點擊進入你的儲存區,選擇 設定 -> 公用開發URL。
會看到一組 S3 API : https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com/${R2_BUCKET_NAME},這個等等 API 上傳時會用到。

接著按下自訂網域(Custom Domains),輸入你想要用來做為圖床的子網域(例如 cdn.你的網域.com)。

按下儲存後,會進行初始化。
等到狀態變成作用中,就代表成功了。

然後用瀏覽器打開https://cdn.easonyc.com/,會看到 Error 404,是正常的,子網域已經成功接上Cloudflare R2 了。

3. 建立 Account API 權杖 #
接著就是建立這個 R2的 Account API 權杖,才能夠透過程式上傳檔案到 R2。
在 API Details 點擊 Manage。
接著按下建立權杖。
將權限設定為系統管理員。
按下權限設定好後,建立權杖。
接著下方會有S3的儲存金鑰,先記起來,日後就不會再看到了。

圖片自動批量上傳至 R2 #
1. 自動批量處理未提交的圖片 #
要搬到 R2 上,當然可以使用 Cloudflare 網頁直接拖拉上傳,但如果圖片很多,還是得需要透過自動化腳本進行批量處理。
透過 Node.js 結合 AWS SDK 與 sharp 套件,可以自動化整個流程:
- 透過
git status --porcelain自動尋找尚未 commit的文章目錄,並自動尋找該文章目錄下的所有圖片。 - 自動將圖片壓縮為
.webp格式,節省 R2 上的空間。 - 上傳至 Cloudflare R2,並維持原本在
content/posts/底下的文章目錄結構。 - 上傳成功後,會自動印出對應的
https://cdn.你的網域.com/...完整網址,直接貼到文章上。
有了這個腳本,寫文章時只要把圖片丟進文章資料夾中,寫完後執行一次腳本,就能快速完成轉檔、上傳並拿到圖片網址。
在開始寫腳本前,我們需要在專案底下建立 .env 檔,並填入剛才在 S3的儲存金鑰 獲得的連線資訊:
R2_ACCOUNT_ID = 你的 Cloudflare 帳戶 ID(可以在 R2 概觀頁面或網址列找到)
R2_ACCESS_KEY_ID = 剛才建立 API 權杖時取得的 存取金鑰 ID
R2_SECRET_ACCESS_KEY = 剛才建立 API 權杖時取得的 機密存取金鑰
R2_BUCKET_NAME = 你的 R2 儲存區名稱(例如 `cdn`)
R2_DOMAIN = 綁定好的 R2 自訂網域(例如 `https://cdn.你的網域.com`)接著,以下為自動化上傳範例:
const fs = require('fs');
const { S3Client, PutObjectCommand } = require('@aws-sdk/client-s3');
require('dotenv').config();
const { R2_ACCOUNT_ID, R2_ACCESS_KEY_ID, R2_SECRET_ACCESS_KEY, R2_BUCKET_NAME } = process.env;
// 初始化 S3 Client (將 Endpoint 指向 Cloudflare R2)
const S3 = new S3Client({
region: "auto",
endpoint: `https://${R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
credentials: {
accessKeyId: R2_ACCESS_KEY_ID,
secretAccessKey: R2_SECRET_ACCESS_KEY,
},
});
// 核心上傳函數
async function uploadToR2(filePath, r2Key, contentType) {
const fileStream = fs.createReadStream(filePath);
const uploadParams = {
Bucket: R2_BUCKET_NAME,
Key: r2Key, // R2 上的檔案路徑
Body: fileStream, // 檔案內容
ContentType: contentType,
};
try {
await S3.send(new PutObjectCommand(uploadParams));
return true;
} catch (err) {
console.error(`❌ 上傳失敗: ${r2Key}`, err);
return false;
}
}
async function processImages() {
// 步驟 1:透過 git 狀態找出尚未 commit 的目錄,並尋找底下圖片
const statusOutput = execSync('git status --porcelain', { encoding: 'utf-8' });
const targetDirs = new Set();
statusOutput.split('\n').forEach(line => {
if (line.includes('content/')) {
targetDirs.add(path.dirname(line.substring(3).trim()));
}
});
const contentDir = path.join(__dirname, 'content');
for (const dir of targetDirs) {
// 尋找圖片 (此處以 glob 為例)
const imageFiles = await glob('**/*.{png,jpg,jpeg}', { cwd: dir, absolute: true });
for (const file of imageFiles) {
// 步驟 2:利用 sharp 自動將圖片轉檔壓縮為 .webp 格式
const tempWebpPath = file.replace(/\.[^/.]+$/, ".webp");
await sharp(file).webp({ quality: 80 }).toFile(tempWebpPath);
// 步驟 3:取得相對路徑以維持結構,並上傳至 R2
const relativePath = path.relative(contentDir, file);
const r2Key = path.join(path.dirname(relativePath), path.basename(tempWebpPath)).replace(/\\/g, '/');
const success = await uploadToR2(tempWebpPath, r2Key, 'image/webp');
// 步驟 4:上傳成功後,印出 CDN 完整網域讓你可以直接複製貼上
if (success) {
const url = `${R2_DOMAIN}/${r2Key.split('/').map(encodeURIComponent).join('/')}`;
console.log(`✅ 上傳成功!`);
console.log(`🔗 URL: ${url}`);
}
}
}
}
// 執行腳本
processImages();2. gitignore 排除圖片 #
透過腳本成功上傳後,確認所有圖片都能正常載入,就可以把文章 push 到 GitHub。透過在 .gitignore 裡,將 content/ 底下的圖片副檔名(如 *.png, *.jpg, *.webp 等)給排除,確保以後 Git 倉庫內只留有純文字,避免 GitHub 倉庫空間過大! gitignore 範例請見上一篇教學的#自動部署
結語 #
透過在 Cloudflare 上購買專屬網域並搭配 Cloudflare R2 建立圖床,不僅能擁有個人化的網址,還能有效減輕 Github Repository 的容量負擔,並借助 CDN 大幅提升圖片載入速度與後續的部署效率,而且擁有自己的網域相比使用 github.io 對於 SEO 也有幫助。