どうも、マイルドインベスターです。 備忘録がてら、WordPressからHugo + Cloudflare Pagesへ移行するための全タスクを、実際の経験(wp2hugoツールとPaperMod採用)に基づいて整理しました。

1. ツール・環境の準備(Windows 11)

まずは、開発環境と外部サービスの準備を整えます。

  • Hugoのインストール: Windows用パッケージマネージャー Winget(標準搭載)を使い、コマンドプロンプトやPowerShellからインストール。
    • コマンド: winget install Hugo.Hugo.Extended
  • Gitのインストール: サイトのソースコードの変更履歴管理とGitHubへの送信のためにインストール。
  • AntigravityまたはVSCodeのインストール: Hugoテーマのカスタマイズ、記事の執筆や設定ファイルの編集に最適なエディタ。
  • GitHubアカウントの作成: サイトのソースコードを保存・管理する場所を確保。
  • Googleフォームの作成: お問い合わせフォームを作成し、完了画面のURLや埋め込みコードを控えておく。

2. WordPressデータの準備(移行元)

既存のWordPressからデータを抽出します。

  • XMLデータのエクスポート: WordPressの管理画面(ツール > エクスポート)を使い、全記事データをXML形式でダウンロードする。

3. Hugoサイトの構築(wp2hugoによる移行)

Windows PC上で、移行ツールを活用してHugoサイトをローカルで確認します。

  • wp2hugoによる一括変換: wp2hugo を実行し、WordPressのXMLからMarkdown形式へ一括変換。この際、定番テーマである PaperMod が自動的に設定されます。
  • 設定ファイル(hugo.yaml)の編集: サイト名、URL構造(Permalinks)を自分の好みに変更する。
  • ローカルプレビュー: hugo server を実行し、ブラウザ(localhost:1313)で表示を確認する。

4. 生成AI(Antigravity/Gemini)による徹底カスタマイズ

今回の移行で最も活躍したのは、AIエージェントのAntigravityとGemini 3.0 Flashです。特にAntigravityには、CSSの細かい微調整や複雑なショートコードのロジック実装を「こうしたい」という言葉だけで依頼し、自律的にコードを生成・修正してもらいました。 カスタマイズした内容をご紹介します。

4.1. 基本設定:Google Analytics と Google AdSense の埋め込み

PaperModでは、layouts/partials/extend_head.html を作成(または編集)することで、<head> タグ内に独自のコードを挿入できます。

  • ads.txtの配置: 旧サイトからads.txtをダウンロードして、static/ フォルダ直下に配置。

Google AdSense

hugo.yaml に定義した googleAdSense パラメータを参照するようにしています。

{{- if site.Params.googleAdSense -}}
<script async
    src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client={{ site.Params.googleAdSense }}"
    crossorigin="anonymous"></script>
{{- end -}}

Google Analytics

こちらは layouts/partials/google_analytics.html を作成し、同様に hugo.yaml からIDを取得します。

{{- if site.Params.googleAnalytics -}}
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id={{ site.Params.googleAnalytics }}"></script>
<script>
    window.dataLayer = window.dataLayer || [];
    function gtag() { dataLayer.push(arguments); }
    gtag('js', new Date());
    gtag('config', '{{ site.Params.googleAnalytics }}');
</script>
{{- end -}}

4.2. お問い合わせページの作成(Googleフォーム)

事前にGoogleフォームで問い合わせフォームを作成し、その埋め込みコードをコピーしておく。 /content/pages/contact-us/index.md を作成し、Googleフォームの埋め込みコードを記述する。

---
title: お問い合わせ
url: /contact-us/
---
お問い合わせは下記のフォームに必要事項を記入して送信してください。

<iframe src="https://docs.google.com/forms/d/..." width="640" height="800" ...>読み込んでいます…</iframe>

4.3. カスタムショートコードの作成

記事内で使い回せる便利なショートコードをいくつか作成しました。ロジック(HTML/Hugoテンプレート)とレイアウト(CSS)の両方をセットで実装しています。

Amazonアフィリエイトリンク

ASINとタイトルを指定するだけで画像を自動取得し、整形するショートコードです。

layouts/shortcodes/amazon.html

{{- $tag := site.Params.amazonJpAffiliate -}}
{{- $asin := .Get "asin" -}}
{{- $title := .Get "title" -}}
<div class="amazon-widget">
    <a href="https://www.amazon.co.jp/gp/product/{{ $asin }}/?tag={{ $tag }}"></a>
    <div class="amazon-widget-img">
        <img src="http://images.amazon.com/images/P/{{ $asin }}.09_SL110_.jpg" />
    </div>

    <div class="amazon-widget-info">
        <span class="amazon-widget-title">
            {{ $title }}
        </span>
        <span class="amazon-widget-via">
            <img src="https://www.amazon.co.jp/favicon.ico" />
            amazon.co.jp
        </span>
    </div>
</div>

assets/css/extended/amazon.css

.amazon-widget {
    margin: 2rem 0;
    max-width: 480px;
    position: relative;
}
.amazon-widget a {
    position: absolute;
    top: 0; left: 0;
    height: 100%; width: 100%;
}
.amazon-widget-img {
    border: 1px solid #E1E8ED;
    border-radius: 15px 15px 0 0;
    text-align: center;
}
.amazon-widget-img img {
    border: none;
    margin: 0 auto;
    max-height: 200px;
    padding: 16px;
}
.amazon-widget-info {
    border: 1px solid #E1E8ED;
    border-top: none;
    padding: 5px 10px 10px 10px;
    border-radius: 0 0 15px 15px;
}
.amazon-widget:hover .amazon-widget-info {
    background-color: #E1E8ED;
}
.amazon-widget-title {
    font-weight: bold;
    display: block;
}
.amazon-widget-via img {
    width: 18px; height: 18px;
    vertical-align: text-bottom;
}

使用例

{{< amazon asin="B0GKLJ7DHD" title="Nintendo Switch 2(日本語・国内専用) + マリオカート ワールド -Switch2 【Amazon.co.jp限定】特典 Nintendo Switch 2 ロゴデザイン マイクロファイバークロス 同梱" >}}
Nintendo Switch 2(日本語・国内専用) + マリオカート ワールド -Switch2 【Amazon.co.jp限定】特典 Nintendo Switch 2 ロゴデザイン マイクロファイバークロス 同梱 amazon.co.jp

WordPress風ブログカード

内部リンクをカード形式で表示します。site.GetPage を使って、タイトルや概要、アイキャッチ画像を自動取得します。

layouts/shortcodes/blogcard.html

{{- $url := .Get "url" -}}
{{- $title := .Get "title" -}}
{{- $img := .Get "image" -}}
{{- $desc := .Get "description" -}}
{{- $target := "_blank" -}}

{{- /* Check if positional argument is provided (path/url) */ -}}
{{- if .IsNamedParams -}}
{{- /* Use named params */ -}}
{{- else -}}
{{- /* Assumes first positional arg is URL/Path */ -}}
{{- $url = .Get 0 -}}
{{- end -}}

{{- $page := "" -}}
{{- if $url -}}
{{- $page = site.GetPage $url -}}
{{- end -}}

{{- if $page -}}
{{- /* Internal Link Logic */ -}}
{{- if not $title -}}{{- $title = $page.Title -}}{{- end -}}
{{- if not $desc -}}{{- $desc = $page.Description | default $page.Summary -}}{{- end -}}
{{- if not $img -}}
{{- with $page.Params.cover.image -}}
{{- $cover_image := . -}}
{{- $resource := $page.Resources.GetMatch $cover_image -}}
{{- if $resource -}}
{{- $img = $resource.Permalink -}}
{{- else -}}
{{- $img = $cover_image | absURL -}}
{{- end -}}
{{- else -}}
{{- with $page.Params.images -}}
{{- $img = index . 0 | absURL -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- /* Use permalink if Page found */ -}}
{{- $url = $page.Permalink -}}
{{- $target = "" -}} {{- /* Internal links often open in same tab, but user might prefer blank if explicitly set */ -}}
{{- end -}}

<a href="{{ $url }}" class="blog-card" {{ if $target }}target="{{ $target }}" rel="noopener noreferrer" {{ end }}>
    <div class="blog-card-content">
        <div class="blog-card-title">{{ $title }}</div>
        <div class="blog-card-desc">{{ $desc | plainify | truncate 80 }}</div>
        <div class="blog-card-site">{{ site.Title }}</div>
    </div>
    {{- if $img -}}
    <div class="blog-card-thumbnail">
        <img src="{{ $img }}" alt="{{ $title }}">
    </div>
    {{- end -}}
</a>

assets/css/extended/blogcard.css

.blog-card {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: var(--entry);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin: 1.5rem 0;
    padding: 1rem;
    text-decoration: none !important;
    color: inherit;
    transition: transform 0.2s, box-shadow 0.2s;
    overflow: hidden;
}
.blog-card:hover {
    transform: translateY(-2px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
    border-color: var(--primary);
}
.blog-card-content {
    flex: 1;
    margin-right: 1rem;
    min-width: 0;
}
.blog-card-title {
    font-weight: bold;
    color: var(--primary);
    margin-bottom: 0.5rem;
}
.blog-card-thumbnail {
    flex-shrink: 0;
    width: 160px;
    background: var(--code-bg);
}
@media (max-width: 600px) {
    .blog-card { flex-direction: column-reverse; }
    .blog-card-thumbnail { width: 100%; margin-bottom: 1rem; }
}

使用例

{{< blogcard "/posts/diary/wordpress-to-hugo/" >}}
WordPressからHugo + Cloudflare Pagesに移行!サーバ代0円と生成AIによる快適ブログ運用
はじめに どうもマイルドインベスターです。 私は、数年間、ブログ運営のにWordPressを使ってきました。しかし、この度思い切って静的サイトジェネレーター …
お金の育て屋さん
WordPressからHugo + Cloudflare Pagesに移行!サーバ代0円と生成AIによる快適ブログ運用

Flourish(動的グラフ)の埋め込み

IDを指定するだけで Flourish のグラフを埋め込めるようになります。

layouts/shortcodes/flourish.html

<div class="flourish-embed" data-src="visualisation/{{ .Get 0 }}"></div>
<script src="https://public.flourish.studio/resources/embed.js"></script>

使用例

{{< flourish "12345678" >}}

X(旧Twitter)のポストの埋め込み

Hugoには標準でXのポストを埋め込むためのショートコードが備わっています。以前は tweet でしたが、現在は x という名前でも利用可能です。

使い方

ポストのURLの最後にあるIDをコピーして指定します。

{{< x "1673907275338104832" >}}

hugo.yaml の設定

デフォルトではXのリッチなウィジェットを読み込みますが、プライバシー保護や表示速度向上のために hugo.yaml で「simple」設定を有効にすることをおすすめします。

privacy:
  x:
    simple: true # 投稿内容をAPI経由で取得せず、シンプルなHTML(引用タグ)のみにする
    enableDNT: true # トラッキングを防止(Do Not Track)

これにより、外部との通信を最小限に抑えつつ、Googleが推奨するセマンティックなマークアップで表示されます。

5.4. デザイン微調整 (CSS)

全般のデザイン調整

assets/css/extended/custom.css に記述することで、見出しやテーブルのスタイルを自分好みに調整しています。

assets/css/extended/custom.css

/* --- H2: メインセクション --- */
.post-content h2 {
    padding: 0.5rem 0 0.5rem 12px;
    border-left: 6px solid var(--secondary);
    border-bottom: 1px solid var(--border);
    background: rgba(var(--secondary), 0.05);
}

/* --- テーブルのデザイン --- */
table {
    width: 100%;
    border-collapse: collapse;
    margin: 2rem 0;
    border: 1px solid var(--border);
}
table thead { background-color: var(--code-bg); }
table th, table td {
    padding: 12px 15px;
    border-bottom: 1px solid var(--border);
}
table tbody tr:nth-of-type(even) {
    background-color: rgba(128, 128, 128, 0.05);
}

フッタのカスタマイズ

標準のフッタをオーバーライドし、独自のリンク集とスタイリングを適用しました。

layouts/partials/footer.html

<footer class="footer">
    <div class="footer-links">
        <a href='{{ "/privacy-policy" | relURL }}'>プライバシーポリシー</a>
        <a href='{{ "/contact-us/" | relURL }}'>お問い合わせ</a>
    </div>
    <span>&copy; {{ now.Year }} <a href='{{ "/" | absLangURL }}'>{{ site.Title }}</a></span>
</footer>

assets/css/extended/footer.css

.footer {
    text-align: center;
    padding: 2rem 0;
}
.footer-links {
    margin-bottom: 1rem;
}
.footer-links a {
    margin: 0 10px;
    color: var(--secondary);
    text-decoration: none;
}
.footer-links a:hover {
    text-decoration: underline;
}

5. 設定ファイル(hugo.yaml)

このサイトでの設定例でs.

baseURL: https://mildinvestor.com/
languageCode: ja
defaultContentLanguage: ja
title: お金の育て屋さん
theme: PaperMod
taxonomies:
  category: categories
  tag: tags
permalinks:
  page:
    posts: /:slug/
params:
  description: お金の不安を感じる方の力になりたい。お金と健全に付き合うための知識と考え方。
  defaultTheme: auto
  disableThemeToggle: false
  showShareButtons: true
  showReadingTime: true
  showToc: true
  tocopen: true
  showBreadCrumbs: true
  showCodeCopyButtons: true
  comments: false
  hideFooter: false
  DateFormat: '2006-01-02'
  mainSections:
    - posts
  assets:
    favicon: /favicon.ico
    disableHLJS: true
  googleAdSense: ご自身のものを記入
  googleAnalytics: ご自身のものを記入
  amazonJpAffiliate: ご自身のものを記入
copyright: "© 2019-2026 お金の育て屋さん"
markup:
  highlight:
    codeFences: true
    guessSyntax: true
    style: monokai
  goldmark:
    renderer:
      unsafe: true
outputs:
  home:
    - HTML
    - RSS
    - JSON
outputFormats:
  RSS:
    mediaType: application/rss+xml
    baseName: feed
menu:
  main:
    - name: 全記事一覧
      url: /all/
      weight: 10
    - name: 検索
      url: /search/
      weight: 20
    - identifier: categories
      name: カテゴリ
      url: /categories/
      weight: 30
    - identifier: tags
      name: タグ
      url: /tags/
      weight: 40
    - name: プロフィール
      url: /profile/
      weight: 50
privacy:
  x:
    simple: true
    enableDNT: true

6. 公開とドメイン設定(Cloudflare連携)

ローカルで動作確認がとれたらサイトを公開します。

  • GitHubへプッシュ: ローカルのHugoプロジェクトをGitHubのリポジトリへアップロード。
  • Cloudflare Pagesと連携:
    • Cloudflare管理画面からWokers/Pagesを選択する。その後「Pages」を選び、HugoサイトのGitHubリポジトリを選択。
    • ビルド設定: フレームワークで「Hugo」を選択する。
  • カスタムドメインの設定: Cloudflareで自分が所有しているドメインを紐づけ。
  • 動作確認: AdSense広告が表示されているか、お問い合わせフォームが機能するかを最終確認。

7. まとめ

今回のカスタマイズを通じて、Hugo は「シンプルでありながら拡張性が非常に高い」ことを実感しました。特に PaperMod のようなメジャーなテーマであれば、生成AIとの協力で思い通りのデザインや機能を効率的に実装できます。