🕵️

Nuxtの動的ルーティングのSSGと静的サイトホスティングの404

2021/09/05に公開

Nuxtの動的ルーティングの静的サイトジェネレート(SSG)と静的サイトホスティングサービス(Netlify、Vercel、GitHub Pages)の404ページの挙動について調べました。

結論からすると、存在しない動的ルーティングで404ページを表示させたい場合はvalidate関数()を設置する必要があります。

やりたいこと

静的ジェネレート後、存在しないIDのURL(例:/blog/9999)の場合は404ページに飛ばしたいです。

前提

/blog/以下が動的ルーティングになっています。動的ルーティングのID番号を拾ってAPIから該当データを引っ張ってくるJamstack構成です。具体的には、/blog/1にアクセスした場合、APIからID1に該当するデータを読み込んで、表示する仕組みです。

404.htmlが生成されるようにするため、nuxt.config.jsに下記設定をします。

generate: {
  fallback: "404.html"
}

適切に動的ルーティング書き出し設定をし、存在するIDのページは静的出力されるものとします。サービス側で404の場合は404.htmlが表示されるように設定します。(ルートディレクトリに404.htmlを置けば、多くのサービスで対応完了です)

静的ジェネレート時に出力されるもの

generateコマンド実行時はIDが存在するページ(例;/blog/1/blog/2/...)のindex.htmlは生成されますが、存在しないページ(例:/blog/9999)のindex.htmlファイルは生成されません。

この状態で静的サイトホスティングへデプロイします。

ケース1:validate()関数を使わない場合

動的ページ側でvalidate()関数を使わない場合の挙動です。

ルーター遷移時

例えば/blog/1から/blog/9999<nuxt-link>を使って移動した場合、動的ルーティングが実行されます。動的ページのテンプレートが読み込まれ、9999に該当するデータを引っ張ってこようとページが表示されます。

ルーター遷移の仕組み(実際にはページ遷移せず、動的にページの内容を書き換える)からすれば、この挙動は意図したものと言えます。

当然ながら該当IDのデータはないので、APIはない旨の値を返します。このとき、適切なエラーハンドリングを行っていないと、場合によってはNuxtのランタイムエラーを引き起こします。エラーが発生するとNuxtのjs処理が中断され、以後サイト全体に不具合が生じることもあります。(例:<nuxt-link>が全体で効かなくなる)こうなってしまうと、ページを再読み込みして初期化しないといけません。

直接URLアクセス

アドレスバーに直接/blog/9999をURL欄に打ち込んだ場合でも、ルーター遷移時と同じような挙動(動的ページのテンプレートが読み込まれ、APIから持ってこようとする)になります。index.htmlがなくても、ページがあるような振る舞いです。ネットワークタブを見るとHTTPステータスコードは404を返していますが、意図した404ページは表示されません。

ケース2:validate()関数を使う場合

validate()関数を使うとURLやクエリなどに応じてルーティングの可否を設定できます。

ルーター遷移時

<nuxt-link>を使って、移動した場合、ページ表示前にvalidate()関数によりチェックが行われ、不適当なURLでは404ページが表示されます。

URL直アクセス時

さきほどのvalidate()関数を使わなかった場合と違い、こちらも意図通り404ページが表示されます。

なお、これらの挙動はローカル環境でgenerateコマンド→startコマンドを実行したときも同じです。

validate()関数を使って不適当なURLを弾く

このように、動的ルーティングを持つサイトを静的サイトホスティングサービスにデプロイし、きちんと404ページを表示したい場合はvalidate()関数によるチェックが必要です。今回はSSGでの挙動の話でしたが、SSRでも不適当なURLをページテンプレート表示前に弾きたい場合はこのvalidate()関数は有効でしょう。SSG、SSRいずれの場合も特別な事情がなければvalidate()関数を使うのが無難です。

validate()関数で気をつけておきたい点

validate()関数内でAPIと通信して照合しているとその分遷移が遅くなります。これは遷移毎にAPIへの通信が発生するためです。nuxtServerInitアクションを使って、予め存在するルーティング一覧をAPIから取得storeに保存し、それと照合させると早いです。

レンタルサーバーの場合はちょっと挙動が違う

ここまでは静的サイトホスティングサービスについての話でしたが、レンタルサーバー(さくらインターネットなど)の場合は少し違います。validate()関数なしでも、存在しないページへのアクセスは404になりました。おそらく、Apacheサーバーを使っているのでindex.htmlの存在しないURLへのアクセスはサーバー側の処理で404エラーになるのでしょう。このとき、用意した404ページではなく、サーバーデフォルトの404ページが表示されます。用意した404ページを表示させたい場合は別途.htaccessなどで制御する必要があります。

Discussion