Cloudflare PagesのPreviewでHugoビルドが失敗し英語ページが404になった原因と対策

Fixing a Hugo Version Mismatch and a Broken English Page on Cloudflare Pages Preview Deployments

* 本ページはプロモーションが含まれています

Hugoの多言語機能で日本語ブログに英語版を別URLで公開した で英語版を/en/配下に公開したあと、メイン以外のブランチをCloudflare PagesのPreviewに出そうとしたら、立て続けに2件のトラブルに遭遇した。
メイン(本番)は問題なく成功していたので、最初は原因が見当も付かなかった。

  1. Previewのビルドがpermalink ill-formedで失敗する
  2. デプロイ後、メニューの「English」をクリックすると404になる

どちらもCloudflare PagesがProductionPreviewで環境変数を別管理していることが根っこにあった。1つずつ潰していく。


1. Previewがデプロイできなかった

症状

メイン以外のブランチをプッシュすると、Cloudflareのビルドが次のエラーで失敗した。

Error: error building site: failed to create resource spec: error expanding "/:contentbasename/": permalink ill-formed
Failed: build command exited with code: 1

メイン(本番)は成功する。

原因

hugo.tomlのパーマリンクは/:contentbasename/にしている。このトークンはHugo 0.144.0で追加されたもので、それより古いHugoは未知のトークンとみなし、permalink ill-formedになる。

デプロイ使う環境変数Hugo結果
メインProductionのHUGO_VERSION0.164.0(0.144以上)成功
他ブランチPreviewのHUGO_VERSION未設定、または0.130.0など古い値permalink ill-formed

このブログをNetlifyから移行した当初はHUGO_VERSION=0.130.0だった。その後テーマ更新で:filename:contentbasenameに置き換え、本番だけ新しいHugoに上げていた。Preview側がそれに追従せず、古いままだった。

対策

Cloudflareダッシュボード→Pagesプロジェクト→Settings → Environment variablesで、PreviewにもProductionと同じHUGO_VERSION(0.144以上。本番と同じ0.164.0が安全)を入れる。
確認は失敗ログ先頭のInstalling Hugo 0.xx.xを見ればよい。0.144未満ならこの原因で確定してよい。

この対策でPreviewビルドは成功した。


2. デプロイ後に英語版が切り替わらなかった

症状

Previewサイトのメニューに「English」は出る。クリックすると404になる。ローカルのhugo serverでは問題が再現しなかった。

原因

英語ページ自体はPreview上に存在していた。リンク先が違っていた。

言語スイッチャーは.Permalink(絶対URL)を使う。

<a class="nav-link" href="https://scribble.washo3.com/en/" lang="en" hreflang="en">English</a>

日本語メニューはrelLangURLなので/aboutのような相対パスになり、Preview域内に留まる。Englishだけhugo.tomlbaseurlhttps://scribble.washo3.com/)向きの絶対URLになっていた。

実際の遷移先はhttps://scribble.washo3.com/en/。本番mainにはまだ/en/が無かったため404になる。

https://<hash>.<project>.pages.dev/en/   -> 200
https://scribble.washo3.com/en/               -> 404

原因の一歩手前は、環境変数CF_PAGES_URLを本番ドメインに手動で上書きしていたことだった。Cloudflareはデプロイごとに正しいPreview URLを自動注入するが、ダッシュボードの値が優先される。ビルドコマンドでhugo -b $CF_PAGES_URLとしていても、その上書きがそのままbaseURLになってしまう。
ローカルで問題が再現しなかったのは、hugo serverbaseURLlocalhostにするためだった。

対処

  1. Previewの環境変数からCF_PAGES_URLの手動設定を外す
  2. ビルドでは$CF_PAGES_URLを素直に使う(Cloudflare注入値をそのまま使う)

この対処でEnglishがPreview内の/en/に向くようになり、404は解消した。
今すぐ見るだけなら、Preview URLの後ろに/en/を直接付けても確認できる。


3. 最終的なビルド構成

環境変数

変数ProductionPreview備考
HUGO_VERSION0.164.0(本番と同じ)0.164.0(本番と同じ)両方必須。0.144未満だと:contentbasenameが死ぬ
CF_PAGES_URL手動設定しない手動設定しないCloudflareがデプロイ URLを自動注入する

Build command

これまでのgit fetch --unshallow && hugo --gc --minify -b $CF_PAGES_URLでも、lastmodとPreviewの英語切替は動く。ただし次の2点が残っていた。

  • git fetch --unshallowは、既にfull cloneだと失敗し&&でHugoまで届かない
  • 本番の$CF_PAGES_URLはカスタムドメインではなく*.pages.devなので、sitemapやhreflangがpages.dev指しになる

そこで、ダッシュボードのBuild command欄には次の1行を入れることにした。

if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then git fetch --unshallow; fi && if [ "$CF_PAGES_BRANCH" = "main" ]; then hugo --gc --minify; else hugo --gc --minify -b "$CF_PAGES_URL"; fi
  • 浅いcloneのときだけunshallowする(enableGitInfoのlastmod用)
  • mainはhugo.tomlbaseurl = https://scribble.washo3.com/をそのまま使う
  • その他のブランチはCloudflare注入の$CF_PAGES_URLを使う

Build command欄は1本のシェルコマンドとして実行されるので、複数行のままは貼れない。;でつなぐか、リポジトリにbuild.shを置いて欄にはbash build.shと書く方法もある。

#!/bin/bash
if [ "$(git rev-parse --is-shallow-repository)" = "true" ]; then
  git fetch --unshallow
fi
if [ "$CF_PAGES_BRANCH" = "main" ]; then
  hugo --gc --minify
else
  hugo --gc --minify -b "$CF_PAGES_URL"
fi

./build.shだと実行ビット不足で失敗することがあるので、bash build.shと書く方が無難らしい。

フラグの意味

要素役割
git fetch --unshallowCloudflareはShallow cloneでチェックアウトする。enableGitInfoのlastmodを正しくするには完全な履歴が必要
hugo --gc --minifyCIの定番。--enableGitInfoはtoml側にあるので付けなくてよい
-b $CF_PAGES_URLPreviewでのEnglishメニュー・hreflang用の絶対URL。本番では使わない

まとめ

項目内容
症状1Previewビルドがpermalink ill-formedで失敗
原因1PreviewのHUGO_VERSIONがProductionより古く、:contentbasenameトークン(Hugo 0.144〜)を解釈できない
対策1PreviewのHUGO_VERSIONをProductionと同じ値に揃える
症状2Preview上のEnglishリンクが本番ドメインへ飛び404
原因2CF_PAGES_URLを本番ドメインに手動上書きしていた
対策2CF_PAGES_URLの手動設定を外し、Cloudflare注入値をそのまま使う

どちらもProductionとPreviewの環境変数が別管理であることを忘れていたのが原因だった。
Cloudflare Pagesを多言語Hugoサイトで使う場合は、環境変数は両方の環境で揃っているか、逆に手を加えすぎていないかを最初に疑うとよさそうだ。

参考

関連項目