Nuxt.jsにおける末尾スラッシュを統一する方法


はじめに

Nuxt.jsにおける簡単そうで簡単ではなかった末尾スラッシュ問題にいくつかの着地点が見えましたのでまとめます。

前提

今回は、静的サイトをNuxt.jsを使って生成するケースのお話です。

Nuxt.jsは何も設定せずに、generateすると、htmlはindex.html(末尾スラッシュが必要)で生成され、sitemapが末尾スラッシュなしで生成されるため、色々と怒られます。

必ずどちらかに統一する作業が必要です。

気をつけるべきポイント

sitemap

@nuxtjs/sitemap を利用して簡単にsitemapを生成することができます。標準では、末尾スラッシュなしで生成されます。この子をちゃんとしないとグーグルさんに結構怒られたりします。

generate設定

静的ページを生成時は、デフォルトではpage名/index.htmlが生成されるため、末尾スラッシュ有りになります。ただし、nuxtさんはこの部分はよしなにしてくれるようで、localhost上だと末尾スラッシュ有りなしどちらでもページにはアクセスができる仕様になっています。

nuxt-link

当たり前に使っている<nuxt-link to="/">ですが、例えばabout.vueページヘのリンクは<nuxt-link to="/about">と記載し、基本的に末尾スラッシュなしで記載しています。
(公式および参考記事を見ても、末尾スラッシュ記載しているものは見たことがありません。知識不足だけかもしれませんんが、、、)

vue-router

基本的にはpageフォルダ内のファイルを認識して末尾スラッシュなしで生成されます。

サーバー側のRedirect処理

最終的に、末尾スラッシュのあり/なし統一のリダイレクト処理を記載する必要があります。

末尾スラッシュありで統一

sitemap

下記1文追加で、sitemap.xml内のすべてのURLの末尾にスラッシュが挿入されます。

nuxt.config.js
export default {
  sitemap: {
    trailingSlash: true, // 追加
    ...
  }
}

generate

初期設定から変更はありません。

vue-router

middlewareにredirect.jsのファイルを作成し、nuxt.config.jsで読み込みます。
末Z

middleware/redirect.js
// 末尾にスラッシュがなければスラッシュ付きのルートへ301リダイレクトする
export default function({ route, redirect }) {
  const path = route.path
  const hash = route.hash
  const type = route.query.type
  if (path.slice(-1) !== '/' && path !== '/404') {
    if (hash) {
      redirect(301, path + '/#' + hash)
    } else if (type) {
      redirect(301, path + '/?type=' + type)
    } else {
      redirect(301, path + '/')
    }
  }
}
nuxt.config.js
export default {
  router: {
    trailingSlash: true,
    middleware: ['redirect'] // 追加
  }
}

これでnuxt-linkが末尾スラッシュなくても強制末尾スラッシュ有りへ遷移します。

サーバー側のRedirect処理

この部分は各自の公開環境に合わせて、リダイレクト処理をかけてもらえればと思います。

さくらなどのレンタルサーバー

Apacheなどのソフトウェアが使用されているサーバー環境では.htaccessにリダイレクト処理を記載します。
未検証なので記載はしませんが、調べればすぐにやり方出てくると思います。

Amplifyコンソール

この子は自動で、/aboutにファイルがない場合は、自動的に/about/index.htmlに301リダイレクト処理をかける仕様になっています。そこでファイルがない時に初めて404エラーになります。
この動作がわかるまで色々沼りましたが、Amplifyコンソールを利用する場合は、現時点では末尾スラッシュ有りがベターかなと思います。

末尾スラッシュなしで統一

以下の項目を対応すれば、SEO対策も含めて問題なく動作するはずです。

sitemap

初期設定から変更はありません。

generate

下記1文追加で、page名/index.htmlの生成はなくなり、page名.htmlになります。

nuxt.config.js
export default {
  generate: {
    subFolders: false, // 追加
    ...
  }
}

vue-router

初期設定から変更はありません。

サーバー側のRedirect処理

Redirect処理はどのサーバーに公開してくるかで変わってきます。

さくらなどのレンタルサーバー

Apacheなどのソフトウェアが使用されているサーバー環境では.htaccessにリダイレクト処理を記載します。

.htaccess
# 末尾のスラッシュ削除
RewriteRule (.*)/$ https://%{HTTP_HOST}/$1 [R=301,L]

NetlifyやAmplifyコンソールなどのホスティングサービス

こいつの場合、どうやって末尾スラッシュのリダイレクト処理を行うか、調べても調べてもわかりませんでした。ホスティングサービスを利用する場合は、素直に末尾スラッシュ有りで設定してもらったほうがいいと思います。(別の方法があればぜひお教えください。)

注意ポイント

わたくしがハマったポイントを共有。

末尾スラッシュ有りパターンでrouterのtrailingSlashは必ずtrueにする

middlewareでリダイレクト処理書いているから、routerのtrailingSlashをtrueに設定する意味なくない?と思ってコメントアウトしていたことがありますが、これは絶対にやってはいけません。

storeの挙動がおかしくなて色々トラブルのもとになります。

動的ページがある場合は注意

動的ページがあるときは、nuxt.config.jsで基本的にはgenerateとsitemapどちらにもpathを書き込む処理を書きます。
このとき、generateとsitemapでのtrailingSlash:trueの動作が違うので注意が必要です。

generateは追加したrouteに対しては、自動で末尾にスラッシュ処理はしてくれませんので、pathを追加する際に末尾スラッシュ有りで追加する必要があります。

逆にsitemapは自動で末尾スラッシュを必ず付けてくれるので、pathを追加する際に末尾スラッシュは不要です。

ここを見逃すと地味にハマりますので、注意が必要です。

終わりに

どちらも現時点においてのとりあえずの実装になっている感がありますので、本当はもっときれいないい方法無いかなと思っております。
この記事を見た方の少しでもお役に立てば何よりです。

参考記事

NuxtアプリケーションでURL末尾にスラッシュを強制的に付ける方法
Nuxtで構築したブログでサイトマップを生成する方法
trailingSlash(Nuxt.js公式)
subFolders(Nuxt.js公式)
リダイレクトを使用する(AWS Amplify公式)