💬

既存サイトのサブディレクトリでNext.jsを運用する

2022/02/02に公開

小ネタです。

サブディレクトリでNext.jsを運用するというのは何を言っているかというと

例えばhttps://example.com/という既存のwebサイトがあり、そのサブディレクトリのhttps://example.com/hogeにアクセスするとその配下ではNext.jsで実装されたhttps://hoge.vercel.appのようなページを表示する

みたいな挙動のことである。

なぜこのような運用が必要なのかは置いておくとしてこういう構造にしたい場合は既存サイト側のプロキシNext.js側の両方で対応をすることになる。

既存サイト側

まず既存サイト側のプロキシサーバー等、例えばnginxやvarnishがwebサーバーの前段にあるだろう。そこでは下記のようなプロキシ設定をする。

location /hoge/ {
  proxy_pass https://hoge.vercel.app/;
}

Next.js側

そもそも上記の設定だけではうまく動かないのかはなぜか?

実際に試すとわかるがhttps://example.com/hogeにアクセスするとプロキシされてhttps://hoge.vercel.app/のhtmlは表示されるのだが、そのページのロード時に呼ばれるcssやjsのような静的ファイルは相対パスなのでhttps://example.com/_next/css/xxxxxxxx.jsのような/hoge/の無いパスにアクセスしに行ってしまうから404になる。結果的にjsやcssの効かないhtmlだけのページが表示されてしまう。

この挙動を修正するにはNext.js側で静的ファイルの相対パスが/hoge/を含む形でセットされる必要があるが、これにはNext.jsのassetPrefixとNext.js 9.5から導入されたrewrites機能を使うと大体は解決できる。

next.config.js
module.exports = {
  assetPrefix: "/hoge",
  async rewrites() {
    return [
      {
        source: "/hoge/api/:path*",
        destination: "/api/:path*",
      },
      {
        source: "/hoge/images/:query*",
        destination: '/_next/image/:query*'
      },
      {
        source: "/hoge/_next/:path*",
        destination: "/_next/:path*",
      },
    ]
  }
}

これで静的ファイルだけでなくimageやapiのpathも/hoge/付きで対応できるようになり/hoge/_next/へのアクセスも通るようになるので大体は期待通りの挙動が実現できる。

当初はこのような挙動を実現するニーズはないだろうということでNext.js側で対応される予定はなかったみたいだが、
https://github.com/vercel/next.js/issues/5602

突如2年後にNext.js 9.5からできるようにしといたわ、みたいな感じで対応されたっぽいのが面白かった。
https://github.com/vercel/next.js/issues/5602#issuecomment-673382891

そういえば大体はうまくいくと言っている理由は、どうも調べていくとgetServerSidePropsを使ったページへLinkを使って遷移するときに/_next/dataへリクエストが飛ぶ場合はうまくいかないと言っている人がいたから。ただ自分の環境で試してみたが再現できなかったのでプロキシサーバーを別途持っていない場合の話かもしれない。
https://arihantverma.com/posts/2021/07/18/workaround-nextjs-data-urls-same-domain-multiple-apps/

その他には<Link href="/" as="/hoge">とかrouter.push("/", "/hoge")みたいに記述しないといけなかったりするのも面倒ではある。自分はWrapしたComponentを作ったりして対応した。Linkやrouter周りのめんどくささを解決するためにbasePathを使う方法も試したがプロキシサーバーとの兼ね合いで断念した。

Discussion