Hugo サイトで、存在しない URL にアクセスされたときの 404 ページを用意する。
Hugo では layouts/404.html を作ると、ビルド時に public/404.html が生成される。ただし、本番環境で Nginx から配信している場合は、Nginx 側にも「404 のときは /404.html を返す」という設定が必要になる。
今回やることは次の二つ。
- Hugo 側で
public/404.htmlを生成する - Nginx 側で 404 時に
/404.htmlを返す
Hugo 側に 404 テンプレートを作る
プロジェクト直下に layouts/404.html を作る。
このサイトでは通常ページと同じレイアウトに載せたいので、main ブロックだけを書いた。
{{ define "title" }}404 Not Found | {{ site.Title }}{{ end }}
{{ define "main" }}
<article class="article not-found">
<header class="article-header entry-header">
<h1 class="entry-title">404 Not Found</h1>
</header>
<div class="entry-content cf">
<p>{{ T "not_found_message" }}</p>
<p><a class="button-link" href="{{ site.Home.RelPermalink }}">{{ T "back_to_home" }}</a></p>
</div>
</article>
{{ end }}
文言は i18n に寄せる。
日本語側の i18n/ja.toml に次を追加する。
[not_found_message]
other = "ページが見つかりませんでした。URLが変わったか、ページが削除された可能性があります。"
[back_to_home]
other = "ホームへ戻る"
英語側の i18n/en.toml にも同じキーを追加する。
[not_found_message]
other = "The page could not be found. The URL may have changed or the page may have been removed."
[back_to_home]
other = "Back to home"
Hugo で生成されるか確認する
テンプレートを追加したら、Hugo をビルドする。
hugo --environment production --minify --cleanDestinationDir
生成結果に public/404.html があるか確認する。
ls -l public/404.html
Windows 側なら PowerShell で確認できる。
Get-Item public\404.html
ここで public/404.html が生成されていれば、Hugo 側の準備は完了。
Nginx で 404.html を返す設定にする
本番環境では、Hugo の公開ディレクトリを Nginx から配信している。
このサイトでは Docker Compose の Nginx コンテナに hugo-public をマウントし、コンテナ内では /var/www/html として見えるようにしている。
その場合、Nginx の server ブロックは次のようにする。
server {
server_name nosubject.io;
root /var/www/html;
index index.html;
error_page 404 /404.html;
location = /404.html {
internal;
}
location / {
try_files $uri $uri/ =404;
}
}
重要なのはこの行。
error_page 404 /404.html;
try_files でファイルが見つからなかった場合、Nginx は 404 を返す。その 404 に対して error_page を指定しておくと、本文として /404.html が返る。
location / は、Hugo の静的ファイルを配信するための設定。
location / {
try_files $uri $uri/ =404;
}
location = /404.html { internal; } は、エラー処理用として内部的に使うための指定。なくても動くことはあるが、404 用のファイルとして扱う意図がはっきりするので入れておいた。
Nginx の設定を反映する
Nginx の設定を書き換えたら、まず構文チェックをする。
cd ~/nosubject-docker
docker compose exec web nginx -t
問題なければ reload する。
docker compose exec web nginx -s reload
サービス名が web ではない場合は、先に docker compose ps で確認する。
docker compose ps
表示とステータスコードを確認する
ブラウザで見た目を確認するだけでなく、curl でステータスコードも確認する。
curl -I https://nosubject.io/this-page-does-not-exist/
期待するのは、ステータスコードは 404 のまま、本文は Hugo の 404.html が返る状態。
HTTP/2 404
ローカルの Nginx コンテナに対して確認する場合は、Host ヘッダーを指定する。
curl -k -I https://localhost/this-page-does-not-exist/ -H 'Host: nosubject.io'
ここで 200 OK にしてしまうと、存在しないページが正常ページ扱いになってしまう。検索エンジンにもよくないので、404 ページを表示しつつ、ステータスコードは 404 のままにする。
Hugo server での見え方に注意する
ローカルの hugo server で 404 ページを見たい場合は、直接 /404.html を開いて確認できる。
http://localhost:1313/404.html
ただし、本番の Nginx と同じエラー処理になるわけではない。
本番で重要なのは、Nginx が存在しない URL に対して public/404.html を返すようになっているかどうか。最終確認は本番に近い Nginx 環境で行う。
うまく表示されないときに見るところ
Nginx の既定エラー画面が出る場合は、404.html がないのではなく、Nginx がそのファイルをエラーページとして使っていない可能性がある。
まず、公開ディレクトリに 404.html があるか確認する。
ls -l ~/nosubject-docker/hugo-public/404.html
次に、Nginx の設定に error_page 404 /404.html; が入っているか確認する。
docker compose exec web nginx -T
try_files $uri $uri/ =404; だけでは、Nginx は 404 ステータスを返すだけで、Hugo の 404.html を本文として使ってくれない。
まとめ
Hugo と Nginx の組み合わせで 404 ページを出す手順は次の通り。
layouts/404.htmlを作るhugoでpublic/404.htmlが生成されることを確認する- 本番の Nginx に
error_page 404 /404.html;を追加する nginx -tで構文チェックする- Nginx を reload する
- 存在しない URL へアクセスして、表示と HTTP ステータスを確認する
静的サイトでは、Hugo がファイルを生成するところまでと、Nginx がエラー時にそのファイルを返すところは別の設定になる。
Hugo 側で public/404.html が生成されているのに本番で Nginx のエラー画面が出る場合は、Nginx の error_page 設定を見るのが近道になる。