Nuxtの動的ルーティングのSSGと静的サイトホスティングの404
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