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の末尾にスラッシュが挿入されます。

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

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

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

```javascript: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 + '/')
    }
  }
}
```

```javascript: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`になります。

```javascript: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末尾にスラッシュを強制的に付ける方法](https://knote.life/entry/nuxt-trailing-slash/)
[Nuxtで構築したブログでサイトマップを生成する方法](https://knote.life/entry/nuxt-blog-sitemap/)
[trailingSlash(Nuxt.js公式)](https://ja.nuxtjs.org/api/configuration-router/#trailingslash)
[subFolders(Nuxt.js公式)](https://ja.nuxtjs.org/api/configuration-generate/#subfolders)
[リダイレクトを使用する(AWS Amplify公式)](https://docs.aws.amazon.com/ja_jp/amplify/latest/userguide/redirects.html)