こんにちは、id:daido1976 です。
今回は Next.js の Static HTML Export で生成したファイルを Lambda@Edge を使わずに CloudFront + S3 にデプロイする方法を紹介します。
前提
Next.js
Next.js のアプリで SSR を行う場合は Vercel やその他の Node.js 実行環境のあるサーバにデプロイする必要がありますが、そうでない場合は Static HTML Export 機能を使って静的ページを出力し、任意の Web サーバにデプロイすることもできます。
Static HTML Export 機能では通常 page/about.tsx
というファイルから /about.html
が生成されますが、これをそのまま S3 にアップロードして CloudFront 経由で /about
にアクセスしても上手く動きません。
Next.js 側で生成されるファイルを /about.html
から /about/index.html
に変える trailingSlash: true という設定があるのでこれで一件落着と思いきや、CloudFront + S3 側でも課題がありました…。
CloudFront + S3
Next.js の Static HTML Export 機能(& trailingSlash: true
)でビルドされた /index.html
と /about/index.html
のように、ルートとサブディレクトリにインデックスドキュメントを含むファイル群をデプロイする際には以下の方法が考えられますが、それぞれに問題があります。
- CloudFront の Default Root Object と オリジンアクセスアイデンティティ を使う
- ルートの index.html は表示できるがサブディレクトリの index.html が表示できない
- S3 の 静的ウェブサイトホスティング 機能を有効にし、インデックスドキュメント を設定する
- ルート、サブディレクトリともに index.html 表示できるが、S3への直接アクセスが禁止できない
👆 の解決策として、以下の記事のように Lambda@Edge などを使って「末尾が "/" で終わっているアクセスのパスに index.html を付与する」というものが多いです。
が、管理するリソースはできるだけ増やしたくないのと、CloudFront の Behavior を使ってマルチオリジン構成にしている時にはパス書き換えのロジックが複雑になってしまうので、できれば Lambda@Edge を使わず CloudFront + S3 だけでやる方法を見つけたいと考えていました。
結論としては、2. S3 の 静的ウェブサイトホスティング 機能を有効にし、インデックスドキュメント を設定する
の方法を使いながら S3 への直接アクセスを禁止することができたので、その方法をご紹介します。
やり方
以下の AWS ナレッジセンターの記事を参考にしました。
ざっくりしたアーキテクチャ図は以下になります。
1. S3 側での静的ウェブサイトホスティングの設定
S3 の静的ウェブサイトを有効にして、インデックスドキュメントを index.html にします。
以下のエンドポイントは後ほど CloudFront の Origin Domain Name に設定する必要があるので控えておきましょう。
2. CloudFront の Origin Domain Name の設定
CloudFront の Origins and Origin Groups
タブから Origin の Edit を押下し、
Origin Domain Name を S3 の Origin ではなく、1
で取得した S3 の静的ウェブサイトのエンドポイント(ドメインの指定なので http://
は不要)にします。*1
ここまでは通常の静的ウェブサイトホスティングの設定と同じです。
3. Referer を使って CloudFront から S3 への直接アクセスを禁止する
[ヘッダー名] の [Origin カスタムヘッダー] に、[Referer] と入力します。[値] には、オリジンに転送するカスタムヘッダーを入力します (S3 バケット)。オリジンへのアクセスを制限するために、ランダムな値または他の人は知らない秘密の値を入力することができます。
ドキュメントに従ってそのまま Origin の編集画面でカスタムヘッダを設定します。
「ランダムな値または他の人は知らない秘密の値を入力することができます」とある通り、この Referer の値は URL ではなく適当な値でも構いません。
加えて、S3 コンソールから上記のカスタム Referer ヘッダーがリクエストに含まれることを条件として、s3:GetObject
を許可するバケットポリシーを追加します。
バケットポリシーの例#特定の HTTP Referer へのアクセスの制限 - Amazon Simple Storage Service
これで Referer ヘッダーに指定の値が含まれていないアクセスを禁止することができます。*2
4. Next.js 側で各ページのファイル名を index.html にする設定
最後に Next.js の設定で trailingSlash: true
を指定して、各ページのファイル名を index.html にします。
https://nextjs.org/docs/api-reference/next.config.js/exportPathMap#adding-a-trailing-slash