WordPress on GCE から Hugo + Cloudflare Pages へ移行した

長年VM上のWordPressで運営してきたブログを、Hugo + Cloudflare Pagesの構成に移行した。もともとCloudflareをCDNとして使っていて、DNSもCloudflare管理だったので、移行先としてはかなり自然な流れだったと移行してみて感じる。

GCEを離れる理由

WordPress + GCEの構成は柔軟ではあるが、維持コストがじわじわ効いてくる。OSやミドルウェアのアップデート、セキュリティパッチ、バックアップ、そしてGCEのインスタンス費用。ブログを書くためにサーバーの面倒を見るのは本末転倒感が強く、またVMとしての障害などもあり面倒になってしまった感じが強い。

静的サイトジェネレーターに移行すれば、これらの運用負荷から解放される。Hugoを選んだのはビルドが圧倒的に速いから。数千記事でも数秒でビルドが完了すると記載があり選定した。また昨今の生成AIの機能と相性が抜群にいいので記事のメンテナンスも今後は楽になると期待している。

移行後の構成

GitHub (Private リポジトリ)
  └─ Hugo ソースコード (Markdown, テーマ, 設定)
       │  git push
Cloudflare Pages
  ├─ 自動ビルド (hugo コマンド実行)
  └─ 自動デプロイ (Cloudflare エッジから配信)
       │  カスタムドメイン
https://roguer.info/

ポイントはローカルでのビルドが不要なこと。記事をMarkdownで書いて git push するだけで、Cloudflare側でHugoのビルドからデプロイまで自動で走る。

Hugoサイトの準備

Hugoのインストールと初期セットアップは省略。テーマはGit submoduleで管理する。

git submodule add https://github.com/CaiJimmy/hugo-theme-stack themes/hugo-theme-stack

hugo.tomlbaseURL は本番ドメインにしておく。

baseURL = "https://roguer.info/"

WordPressからの移行の場合、既存のパーマリンク構造を維持するためにHugoのaliases機能で旧URLからのリダイレクトを設定しておくとよい。

.gitignore

# ビルド成果物(Cloudflare Pages側で生成する)
public/
resources/_gen/

# Hugoのローカルサーバー用
.hugo_build.lock

# OS系
.DS_Store
Thumbs.db

public/ はビルド成果物なので必ず除外する。ビルドはCloudflare Pages側でやるのでリポジトリに含める必要はない。

GitHubにpush

cd your-hugo-site
git init
git add .
git commit -m "initial commit"
git remote add origin [email protected]:yourname/your-blog.git
git push -u origin main

リポジトリはPrivateで問題ない。Cloudflare PagesはOAuth連携でPrivateリポジトリにもアクセスできる。

テーマをsubmoduleで管理している場合、.gitmodules が含まれるのでそのままpushすればよい。

Hugoのバージョン確認

hugo version
hugo v0.155.3+extended+withdeploy darwin/arm64

バージョン番号(0.155.3)と +extended の有無を控えておく。Cloudflare Pagesのビルド設定で使う。

Cloudflare Pagesでプロジェクト作成

ここが少しわかりにくかった。CloudflareのダッシュボードはWorkerとPagesが統合されていて、うっかりWorkerを作りそうになる。

  1. Cloudflare Dashboardにログイン
  2. 左サイドバーの 「コンピューティング と AI」「Workers & Pages」 を開く
  3. 「アプリケーションを作成する」 をクリック
  4. ここでWorkerの作成画面になるが、Pagesではない。 画面最下部の 「Pagesを導入しようとお考えですか? 始める」 のリンクをクリック
  5. 「既存のGitリポジトリをインポートする」「始める」
  6. GitHubアカウントを認証し、対象リポジトリを選択

手順4が罠。見落とすとWorkerを作ってしまう。

ビルド設定

リポジトリを選択するとビルドの設定画面になる。

項目 設定値
プロジェクト名 任意(デフォルトでOK)
プロダクション ブランチ main
フレームワーク プリセット Hugo
ビルド コマンド hugo
ビルド出力ディレクトリ public

環境変数に以下を追加する。

変数名
HUGO_VERSION 0.155.3(ローカルと同じバージョン)
HUGO_EXTENDED true(extended版の場合)

HUGO_VERSION を指定しないとCloudflare側のデフォルト(古い場合がある)でビルドされる。テーマによってはこれでエラーになるので注意。

設定したら 「保存してデプロイする」 をクリック。

デプロイ確認

初回ビルドが走り、成功すると プロジェクト名.pages.dev のURLでサイトが公開される。このURLでページの表示、リンク、画像あたりをひととおり確認しておく。

カスタムドメインの割り当て

動作確認ができたら本番ドメインを割り当てる。

  1. Pagesプロジェクトの 「カスタムドメイン」 タブを開く
  2. ドメイン(例: roguer.info)を入力
  3. DNS レコードの変更確認画面が出る

CloudflareがDNSレコードをPages向けに自動で書き換えてくれる。GCEを指していたAレコードがPagesのCNAMEに変わる形。

タイプ 名前 コンテンツ
既存レコード A @ 35.xxx.xxx.xxx(GCEのIP)
新規レコード CNAME @ xxxx.pages.dev

「ドメインをアクティブにする」 をクリックするとDNSが切り替わる。同じCloudflareアカウント内での操作なので、ダウンタイムはほぼゼロで完了した。

旧サーバーの停止

しばらく様子を見て問題なければGCEインスタンスを削除する予定。一旦VMは停止。

移行後の運用

記事公開のフローは以下の通り

  1. ローカルでMarkdownを書く(Claude Codeを利用して /new-post , /review-post を積極的に活用)
  2. hugo server でプレビュー
  3. git add . && git commit && git push
  4. Cloudflare Pagesが自動でビルド&デプロイ
  5. 数十秒〜数分で公開完了

これでサーバは特に管理せず、また文書自体もGithubとローカルに残ることになるので安心感が増した。

Hugo で構築されています。
テーマ StackJimmy によって設計されています。