Hugoで作ったブログをCloudflare Pagesにデプロイするまでにやったこと
はじめに
皆様こんにちは、あかいけです。
最近 Hugo でブログを作ったらいい感じだったので、共有します。
皆さんも情報発信の一手段として始めてみてはいかがでしょうか。
今回作ったもの
リクガメとIT技術に関するブログを作りました。
以下リンクからアクセスできます。
やりたいことと実現方法
まずブログを作るにあたって、以下の要件と実現方法を整理しました。
色々書いていますが、個人的に一番大事なのは運用コストかなと思います。
| やりたいこと | 実現方法 | 理由 |
|---|---|---|
| カスタムドメインを使いたい | Cloudflare | ドメイン管理とDNS設定が一箇所で完結できる |
| できるだけ安く運用したい | Cloudflare Pages + R2 | 無料枠が充実しており、個人ブログ規模なら無料で運用可能の見込み |
| レスポンスを早くしたい | Cloudflare | 世界中にエッジサーバーがあり、CDN配信で高速できそう |
| ビルドを早くしたい | Hugo | 1万ページでも10秒以内にビルド可能(らしい) |
| 簡単にデプロイしたい | GitHub + Cloudflare Pages | pushするだけで自動デプロイ、複雑なCI/CD設定が不要 |
| マークダウンで記事を書きたい | Hugo | 標準機能でマークダウンをHTMLに変換できる |
なぜ Hugo + Cloudflare なのか
ブログを作るのはいいとして、
なぜHugoとCloudflareという組み合わせなのでしょうか?
SSGを使いたいだけならNext.jsでもGatsbyでもDocusaurusでもAstroでもいい気がしますし、デプロイ先としてもVercelやAWS、それこそGitHub Pagesでも良さそうな気がします。
もちろん他の選択肢でも問題ないのですが、今回は以下の理由からこの組み合わせを選びました。
Hugoを使う理由
ビルドが早い
HugoはGo言語で書かれており、ビルド速度が圧倒的に速いらしく、公式ドキュメントでも以下のように記載されています。
(今回他のフレームワークと比較はしていませんが、検索すると速度を比較した記事がいくつか出てきます)
Hugo renders a complete site in seconds, often less.
執筆中にファイルを保存するたびにビルドが走りますが、Hugoなら一瞬で反映されるのでストレスがありません。
シンプル
Hugoは静的サイトジェネレーター(SSG)に特化しています。
SSGではサーバーサイドの処理やデータベースは必要なく、生成されるのは純粋な静的HTMLファイルのみです。
そして今回のような静的コンテンツがメインのブログならSSGで十分と判断しました。
逆に言えばSPAやSSRやISRなどJavaScriptをバリバリ使ったりサーバーサイドでの処理をしたい場合は、
他のフレームワークを使いましょう。
テンプレートが豊富
コミュニティで大量のテーマが公開されており、デザインセンスがなくてもいい感じのブログが作れます。
今回使用したterminalテーマもその一つです。
Cloudflareを使う理由
無料枠が充実している
Cloudflare Pagesの無料枠は個人ブログには十分すぎるスペックがあります。
公式ドキュメントの抜粋です。
You can build up to 500 times per month on the Free plan.
Cloudflare Pages sites can contain up to 20,000 files.
| 項目 | 無料枠 |
|---|---|
| ビルド回数 | 500回/月 |
| 同時ビルド | 1 |
| 帯域幅 | 無制限 |
特に帯域幅が無制限なのが嬉しいポイントです。
もしアクセスが急増しても追加料金を気にする必要がありません。
またCloudflare R2も同様に以下の無料枠があります。
10GB/月は無料であり、これも個人ブログであれば十分でしょう。
全体構成
今回構築するブログの全体構成は以下のとおりです。
- 開発環境: Dev Containers (VSCode)
- SSG: Hugo
- ホスティング: Cloudflare Pages
- 画像配信: Cloudflare R2 + カスタムドメイン
- ソースコード管理: GitHub
GitHubにpushするとCloudflare Pagesが自動でビルド・デプロイを行い、画像はR2から配信される構成です。
構築していく
それでは実際に構築していきましょう。
開発環境を用意する
Hugoの開発環境を用意します。
インストール方法はいくつかあり、OSごとのインストール方法は以下ドキュメントに記載されています。
今回はDev Containersを使います。
Dev Containersを使うことで、ローカル環境を汚さずにHugoの開発環境を構築できます。
まずは以下のような設定ファイルを作っておきます。
{
"name": "tortoise-tech-blog-devcontainer",
"image": "mcr.microsoft.com/devcontainers/base:ubuntu",
"features": {
"ghcr.io/devcontainers/features/go:1": {
"version": "1.18"
},
"ghcr.io/devcontainers/features/docker-in-docker:2.13.0": {
"version": "latest",
"moby": true
}
},
"forwardPorts": [1313],
"postCreateCommand": "hugo version"
}
次にVSCodeの左下にあるリモート接続アイコンをクリックします。

表示されるメニューから「コンテナーで再度開く」を選択します。新しいVSCodeウィンドウが開きます。

初回はDockerイメージのダウンロードが必要なので、通信環境によっては起動まで数分程度時間がかかります。
以下のような表示が出たらセットアップ完了です。

ターミナルを開くと、コンテナ内で起動していることが確認できます。
ユーザー名はデフォルトだとvscodeになります。

Hugo 初期セットアップ
プロジェクト作成
以下のコマンドでHugoプロジェクトを作成します。
hugo new site blog;
cd blog;
git init;
git branch -m main;
デフォルトのディレクトリ構成
プロジェクトを作成すると、以下のようなディレクトリ構成が生成されます。
.
├── archetypes
│ └── default.md
├── assets
├── content
├── data
├── hugo.toml
├── i18n
├── layouts
├── static
└── themes
各ディレクトリ・ファイルの役割は以下のとおりです。
| ディレクトリ/ファイル | 説明 |
|---|---|
archetypes/ |
記事のテンプレートを格納。hugo newで記事を作成する際に使用される |
assets/ |
SCSSやTypeScriptなど、ビルド時に処理されるファイルを格納 |
content/ |
ブログ記事やページのマークダウンファイルを格納 |
data/ |
サイト全体で使用するデータファイル(JSON, YAML, TOML)を格納 |
hugo.toml |
Hugoの設定ファイル |
i18n/ |
多言語対応用の翻訳ファイルを格納 |
layouts/ |
カスタムレイアウトテンプレートを格納 |
static/ |
画像やCSS、JavaScriptなど、そのまま配信される静的ファイルを格納 |
themes/ |
テーマを格納 |
テーマ追加
Hugoはコミュニティで様々なテーマが公開されています。
今回は以下のterminalテーマを使ってみます。
ターミナル感が中々良くて、見ていて心が安らぐ気がします。
テーマの追加方法はgit cloneする方法とサブモジュールとして追加する方法があります。
既存のリポジトリに追加したいので、今回はサブモジュールとして利用します。
git submodule add -f https://github.com/panr/hugo-theme-terminal.git themes/terminal
Hugoの設定ファイルに theme を追加します。
これで追加したテーマが反映されます。
baseURL = 'https://example.org/'
languageCode = 'en-us'
title = 'My New Hugo Site'
theme = 'terminal'
記事の作成
Hugoではhugo new contentコマンドで記事を作成できます。
このコマンドを実行すると、archetypes/にあるテンプレートを元に新しいマークダウンファイルが生成されます。
hugo new content content/posts/post-1.md;
hugo new content content/posts/post-2.md;
hugo new content content/posts/post-3.md;
コマンドを実行すると、以下のようなファイルが作成されます。
ファイルの先頭にある+++で囲まれた部分は「Front Matter」と呼ばれ、記事のメタデータを定義します。
+++
title = "Post 1"
date = "2026-01-10T10:18:53Z"
#dateFormat = "2006-01-02" # This value can be configured for per-post date formatting
author = ""
authorTwitter = "" #do not include @
cover = ""
tags = ["", ""]
keywords = ["", ""]
description = ""
showFullContent = false
readingTime = false
hideComments = false
+++
主要なFront Matterフィールドの説明は以下のとおりです。
| フィールド | 説明 |
|---|---|
title |
記事のタイトル |
date |
記事の作成日時(ISO 8601形式) |
author |
著者名 |
cover |
カバー画像のパス |
tags |
記事に付けるタグ(配列) |
description |
記事の説明文(メタタグに使用) |
draft |
trueにすると下書き状態となり、本番ビルドから除外される |
Front Matterの詳細は公式ドキュメントを参照してください。
ローカルサーバー立ち上げ
Hugoには開発用のローカルサーバーが組み込まれています。
以下のコマンドで起動できます。
hugo server -D
| オプション | 説明 |
|---|---|
-D / --buildDrafts |
下書き状態(draft: true)の記事も表示する |
-F / --buildFuture |
公開日が未来の記事も表示する |
サーバーが起動したら、ブラウザで以下のURLにアクセスします。
http://localhost:1313/
Hugoの開発サーバーにはLive Reload機能があり、ファイルを保存すると自動的にブラウザがリロードされます。
これにより、変更内容をリアルタイムで確認しながら記事を執筆できます。嬉しいですね。

カスタマイズ
ここからはterminalテーマをベースにHugoの設定をカスタマイズしていきます。
テーマカラーの変更
terminalテーマでは、公式がカラーパレットジェネレーターを提供しています。
このツールを使うと、好みの配色でCSSファイルを簡単に生成できます。
生成したCSSファイルと、サイトのアイコンなどはstatic/ディレクトリに配置します。
static/に配置したファイルは自動的にビルド時に含まれます。
/static/terminal.css # テーマのカスタムCSS
/static/favicon.png # ブラウザタブに表示されるアイコン
/static/og-image.png # SNSでシェアされた際に表示される画像
| ファイル | 説明 |
|---|---|
terminal.css |
テーマカラーを上書きするカスタムCSS |
favicon.png |
ブラウザのタブやブックマークに表示されるアイコン |
og-image.png |
Open Graphプロトコル用の画像。SNSでリンクをシェアした際のプレビューに使用される |
hugo 設定ファイル
Hugoの設定はTOML、YAML、JSONのいずれかの形式で記述できます。
デフォルトではhugo.tomlが使用されます。
設定項目が多いですが、ほとんどはテーマの公式ドキュメントの「How to configure」を参考にしています。
以下に今回作成した設定ファイルは以下の通りです。
baseurl = "https://your-domain.com/"
languageCode = "ja"
theme = "terminal"
pagination.pagerSize = 5
copyright = "© 2026 リクガメてっく。"
rssLimit = 20
[outputs]
home = ["HTML", "RSS"]
section = ["HTML", "RSS"]
taxonomy = ["HTML", "RSS"]
[markup.highlight]
noClasses = false
[params]
contentTypeName = "posts"
showMenuItems = 3
showLanguageSelector = false
fullWidthTheme = false
centerTheme = true
autoCover = true
showLastUpdated = false
dateFormat = "2006年01月02日 15:04"
[params.twitter]
creator = "lamaglama39"
site = "lamaglama39"
[languages]
[languages.ja]
languageName = "日本語"
title = "リクガメてっく。"
[languages.ja.params]
subtitle = "癒やしのリクガメとIT技術のブログです。"
readMore = "続きを読む"
readOtherPosts = "他の記事を読む"
newerPosts = "新しい記事"
olderPosts = "古い記事"
missingContentMessage = "ページが見つかりません..."
missingBackButtonLabel = "ホームに戻る"
minuteReadingTime = "分で読めます"
words = "文字"
[languages.ja.params.logo]
logoText = "リクガメてっく。"
logoHomeLink = "/"
[languages.ja.menu]
[[languages.ja.menu.main]]
identifier = "about"
name = "このブログについて"
url = "/about"
weight = 1
[[languages.ja.menu.main]]
identifier = "tags"
name = "タグ一覧"
url = "/tags"
weight = 2
[[languages.ja.menu.main]]
identifier = "rss"
name = "RSS"
url = "/index.xml"
weight = 3
- 設定セクションの説明
| セクション | 説明 |
|---|---|
| トップレベル設定 | baseurl, languageCode, themeなどサイト全体の基本設定。baseurlはデプロイ先のURLに合わせて設定する |
pagination.pagerSize |
1ページあたりの記事表示数 |
rssLimit |
RSSフィードに含める記事の最大数 |
[outputs] |
各ページタイプで生成する出力形式を指定。HTMLとRSSを生成する設定 |
[markup.highlight] |
シンタックスハイライトの設定。noClasses = falseでCSSクラスベースのハイライトを使用 |
[params] |
テーマ固有のパラメータ。terminalテーマのレイアウトや表示オプションを設定 |
[params.twitter] |
TwitterカードのメタタグにTwitterアカウント情報を埋め込む設定 |
[languages] |
多言語対応の設定。日本語のラベルやメニュー項目をカスタマイズ |
- params セクションの詳細
[params]セクションはテーマ固有の設定を行えます。
terminalテーマでは以下の項目が設定可能です。
| パラメータ | 説明 |
|---|---|
contentTypeName |
コンテンツのデフォルトタイプ名 |
showMenuItems |
ヘッダーに表示するメニュー項目数 |
centerTheme |
コンテンツを中央揃えにするかどうか |
autoCover |
記事のカバー画像を自動検出するかどうか |
dateFormat |
日付の表示形式(Go言語の時刻フォーマット形式で指定) |
その他、設定値に関する詳細は公式ドキュメントを参照してください。
これらの設定を反映すると以下の見た目になりました。
カスタマイズといっても大した変更はなく、テーマカラーとメニューをいくつか足したぐらいですね。

Cloudflareへのデプロイ
次にCloudflareへのデプロイをしていきます。
まずはソースコードを管理するGitHubリポジトリを作成します。
GitHubリポジトリ作成
GitHubで新規リポジトリを作成します。
リポジトリ名は任意ですが、ブログ名に合わせておくとわかりやすいです。

pushする前に、.gitignoreを作成して、
ビルド成果物などをコミット対象から除外しておきます。
# Generated files by hugo
**/public/
**/resources/_gen/
**/assets/jsconfig.json
**/hugo_stats.json
# Executable may be added to repository
**/hugo.exe
**/hugo.darwin
**/hugo.linux
# Temporary lock file while building
**/.hugo_build.lock
リポジトリにpushしておきます。
git remote add origin git@github.com:Lamaglama39/tortoise-tech-blog.git
git branch -M main
git push -u origin main
Cloudflare Pages作成
次にCloudflare Pagesを作成します。
Hugoのビルド設定については公式ドキュメントに詳しく記載されています。
Cloudflareのダッシュボードから「Workers & Pages」を選択し、「アプリケーションを作成する」をクリックします。

ちょっと分かりづらいんですが、
画面下の Looking to deploy Pages? の「Get started」をクリックします。

「既存の Git リポジトリをインポートする」を選択します。

先ほど作成したリポジトリを選択します。

ビルド設定を行います。
フレームワークプリセットで「Hugo」を選択すると、ビルドコマンドと出力ディレクトリが自動で設定されます。
設定が完了したら「保存してデプロイする」をクリックします。

しばらくすると、以下のようにデプロイが完了します。

デフォルトではドメイン名は「プロジェクト名.pages.dev」となります。
ドメインにアクセスして、正常に表示されることを確認しましょう。

カスタムドメインの設定
次にカスタムドメインを設定します。
Cloudflare Pagesの設定画面から「Custom domains」タブを開きます。

使いたいドメイン名を入力します。
今回は取得しているドメインのサブドメインで設定します。

DNSレコードの設定内容が表示されるので、
問題なければ「ドメインをアクティブにする」をクリックします。

CloudflareでDNSを管理している場合、DNSレコードが自動的に追加されます。
数分後には以下のようにアクティブになりました。

ただしDNSレコードがグローバルに反映されるまでには時間がかかります。
そのため反映されるまでは、設定したカスタムドメインを検索しても以下のようなエラーが出ます。
また今回の場合は設定したカスタムドメインでアクセスできるようになるまで、20分程度かかりました。

カスタムドメインを設定したら、次にHugoの設定ファイルでベースURLを修正する必要があります。
また末尾にスラッシュ(/)が必要な点に注意が必要です。
baseurl = "https://tortoise-tech-blog.lamaglama39.dev/"
画像ファイルはどこで保存する?
さて、記事に乗せる画像ファイルですが、
デフォルトでは他のフレームワークなどと同様に ./static 配下に配置します。
また記事からの参照方法は以下の通りです。
### Markdown 記法

### Shortcode 記法
{{< figure src="/about/macaroni.jpg" title="macaroni" width="50%" height="50%" >}}
![[Screenshot from 2026-01-11 01-23-14.png]]
しかしこの方法には一つ問題点があります。
それは Cloudflare Pages のビルド後に含められるファイル数に上限ある ことです。
Pages uploads each file on your site to Cloudflare's globally distributed network to deliver a low latency experience to every user that visits your site. Cloudflare Pages sites can contain up to 20,000 files.
2万ファイルなのでよほどハードにブログを書かない限りは問題ないと思いますが、記事数と画像ファイル数を合わせると意外と上限に触れそうでちょっと怖いです。
また、画像ファイルをリポジトリに含めると、リポジトリサイズが肥大化してしまう問題もあります。
この対策として、Cloudflare Pages外に画像ファイルを保存して、ブログ内でそれを参照する方法があります。
いくつかの方法がありますが、今回はシンプルにR2 + カスタムドメインで配信する方法を採用します。
- Cloudflare Images
- R2 + カスタムドメイン
- R2 + Workers
またR2のパブリック配信方法には以下の2つがあります。
- カスタムドメイン: 独自ドメインで配信(推奨)
- パブリック開発URL: Cloudflareが提供するURLで配信
今回はカスタムドメインを使用します。
まずR2バケットを作成します。

バケット名を入力して作成します。

バケットが作成されたら、画像ファイルをアップロードします。

次にR2バケットにカスタムドメインを設定します。
バケットの設定画面から「設定」を開いて、カスタムドメインの「追加」をクリックします。

画像配信用のサブドメインを入力します。

自動設定されるDNSレコードが表示されるので、問題がなければカスタムドメインを設定します。

設定が完了すると、カスタムドメイン経由でR2の画像にアクセスできるようになります。

試しにアップロードした画像は、以下のURLでアクセスできるようになっています。
https://image.tortoise-tech-blog.lamaglama39.dev/macaroni.jpg
記事ではそのURLを参照すればOKです。
### Markdown 記法

### Shortcode 記法
{{< figure src="https://image.tortoise-tech-blog.lamaglama39.dev/macaroni.jpg" title="macaroni" width="50%" height="50%" >}}
CORSについて
HugoはSSGなので、生成されるのは純粋なHTMLファイルのみです。
JavaScriptからR2の画像を動的に取得するわけではないため、CORS設定は不要です。
もしJavaScriptでR2のリソースにアクセスする必要がある場合は、CORS設定を追加する必要があります。
さいごに
以上、Hugo + Cloudflare Pagesでブログを構築するまでにやったことでした。
今回の構成のポイントをまとめると以下のとおりです。
- Hugo: ビルドが高速でシンプル。マークダウンで記事を書ける
- Cloudflare Pages: 無料枠が充実。帯域幅無制限でGitHub連携も簡単
- Cloudflare R2: 画像を外部に配置することでPagesのファイル数制限の対象外にできる
この構成なら、よほどハードにブログを書かない限り無料で運用できます。
個人ブログを始めてみたいけどコストが気になる…という方にはピッタリの選択肢だと思います。
ブログを書くことで自分の学びを整理できますし、きっと同じ問題で困っている誰かの助けになります。
皆さんも心当たりはありませんか?エラーで困っていたとき、検索結果に現れた謎の個人ブログに救われた経験が…。
次は皆さんが誰かを救う番です、ぜひ個人ブログを始めてみてはいかがでしょうか。








