Hugo と GitLab で静的 Web サイトを作る

はじめに

Djangoや Northflankで作るのは、DB を持ち処理が動く Web システムでした。一方、会社案内・サービス紹介・ドキュメント・コラムのように、更新頻度が低く、サーバー側で動く処理が要らないサイトは、静的サイトが向いています。速く、安く、壊れにくいためです。

この記事では Hugo(静的サイトジェネレーター)でサイトを作り、GitLab のリポジトリと GitLab Pages で公開する流れを整理します。実はこのサイト自体がその構成です。

静的サイトジェネレーターとは

Markdown で書いた原稿と、HTML のテンプレートを組み合わせて、素の HTML ファイルを事前に生成する仕組みです。配信するのは出来上がった HTML/CSS/JS だけなので、実行環境も DB も要らず、CDN に置けば高速で、攻撃対象も小さくなります。

Hugo は Go 製で、ビルドが非常に速いのが特徴です。数百ページでも一瞬で生成されます。

Hugo の構成

プロジェクトはおおむね次のディレクトリでできています。

サイト/
├── hugo.toml        … サイト全体の設定(baseURL、テーマ、パラメータ)
├── content/         … 原稿(Markdown)。ここの構造が URL 構造になる
├── layouts/         … テンプレート。テーマの上書きもここ
├── themes/          … 導入したテーマ
├── static/          … そのまま配信されるファイル(画像・CSS など)
└── public/          … `hugo` で生成される出力(Git 管理はしない)
  • hugo server … ローカルにプレビュー用サーバーを立てる。保存すると即座に反映
  • hugo … public/ に本番用の HTML を生成

コンテンツの書き方

各 Markdown ファイルの先頭に フロントマター(メタ情報)を書きます。

+++
title = 'ページタイトル'
date = '2026-09-09T09:00:00+09:00'
draft = false
description = '検索結果や SNS に出る要約'
+++

本文をここに書く。
  • content/about.md → /about/、content/column/foo.md → /column/foo/ のように、ディレクトリがそのまま URL になる
  • hugo new column/bar.md で雛形から新規作成
  • draft = true の記事は本番ビルドに含まれない。未来の date も既定では公開されない

テンプレートとテーマ

表示は Go のテンプレートで組みます。基本の 3 つは、

  • baseof.html … 全ページ共通の骨組み
  • list.html … 一覧ページ(セクションのトップなど)
  • single.html … 個別ページ

共通部分は partials/ に切り出します。テーマを導入し、変えたいテンプレートだけを自分の layouts/ に置くと上書きできます。CSS も、テーマの上に自分の 1 枚を重ねる形にしておくと管理が楽です。

GitLab に置いて公開する

リポジトリを GitLab に作り、.gitlab-ci.yml を置くと、GitLab CI がビルドし、GitLab Pages が public/ を配信します。このサイトで使っている設定はおおよそ次のとおりです。

# Hugo extended + Dart Sass + Node.js + git を同梱するイメージ
image: hugomods/hugo:dart-sass-node-git-0.161.1

variables:
  HUGO_ENV: production
  TZ: Asia/Tokyo
  GIT_DEPTH: "0"          # .Lastmod / .GitInfo 用に全履歴を取得

cache:
  key: "$CI_COMMIT_REF_SLUG"
  paths:
    - .hugo_cache/
    - node_modules/

pages:
  stage: deploy
  script:
    - npm ci
    - hugo --gc --minify
  artifacts:
    paths:
      - public
  rules:
    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH

ポイントは次の点です。

  • pages という名前のジョブで public/ を artifact に出すと、GitLab Pages がそれを公開する
  • イメージのバージョンを固定する。Hugo は挙動が変わることがあるため、CI とローカルで同じ版に揃える(Docker の考え方と同じ)
  • GIT_DEPTH: "0":Hugo でページの更新日をコミット履歴から取る(enableGitInfo)には全履歴が要る。浅いクローンだと .Lastmod が正しくならない
  • キャッシュ:.hugo_cache/ と node_modules/ を残すとビルドが速い
  • rules:デフォルトブランチへの push のときだけ公開する

独自ドメインと HTTPS

GitLab の Settings → Pages で独自ドメインを追加し、DNS を検証すると、Let’s Encrypt の証明書が自動で発行・更新されます。hugo.toml の baseURL は本番ドメインに合わせておきます。

ローカル開発

hugo server を直接動かすのが最も手軽ですが、CI と同じ Hugo イメージを Docker で動かしてプレビューする手もあります。こうすると、手元・CI・本番でビルド環境が完全に一致します。

docker run --rm -it -p 1313:1313 -v "$PWD":/src \
  hugomods/hugo:dart-sass-node-git-0.161.1 \
  server --bind 0.0.0.0

レビューの進め方

原稿の追加・修正は ブランチと Merge Requestで進めます。差分がレビューでき、合意したら default ブランチへ merge、CI が走って本番へ反映、という流れです。公開前に確認したいときは、ブランチのビルド結果を artifact から取得するか、レビュー用の Pages 環境を用意します。

関連記事

まとめ

  • 動く処理の要らないサイトは静的サイトが有利。Hugo は Markdown + テンプレートで素の HTML を高速生成する。
  • content/ の構造が URL、フロントマターがメタ情報。テーマは上書きで必要な所だけ変える。
  • GitLab では pages ジョブで public/ を artifact に出せば Pages が配信。イメージ固定・GIT_DEPTH:0・キャッシュが要点。
  • 独自ドメインと HTTPS は Pages の設定で完結。更新はブランチ+ Merge Request で。

このコラムも、記事の Markdown を書いて push するだけで、CI がビルドして公開する ── という形で運用しています。

LaTeX組版・教材校正のご依頼・ご相談はコレクトコネクトへお問い合わせください。

← コラム一覧へ