1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

npm buildをしてS3にアップロードしてCloudFrontで配信するNext.jsプロジェクトがルートページ以外で403 Forbiddenになる

Posted at

Next.js の静的サイトを Amazon S3 にデプロイし、CloudFront を介して配信する際、ルートページ以外のパスで 403 Forbidden エラーが発生することがあ離ました...
この問題が発生する理由と、CloudFront Functions を使用した解決策を調査してみました.

何が起きている?: 403 Forbidden エラー

スクリーンショット 2025-09-29 15.05.36.png

デフォルトでは、ユーザーが https://example.com/about/ のような URL にアクセスすると、CloudFront は S3 バケットから about/ オブジェクトをリクエストします。しかし、S3 はオブジェクトストレージサービスであるため、デフォルトでは about/index.html ファイルを配信するディレクトリとして扱いません。
スクリーンショット 2025-09-29 15.45.45.png

Next.js は、静的エクスポート (next build && next export) でビルドされると、各ルートの HTML ファイルを生成します。例えば、pages/about.jsout/about.html としてコンパイルされます。ユーザーが /about/ をリクエストしても、CloudFront はこれを自動的に /about.html/about/index.html (Next.js で trailingSlash を設定している場合) に書き換えません。その結果、CloudFront は存在しないオブジェクトを S3 に要求し、403 Forbidden エラーが発生します。

ルートパス (/) は、通常 CloudFront のデフォルトルートオブジェクト (多くの場合 index.html) が正しく提供されるため、問題なく動作します。

解決策: URI 書き換えのための CloudFront Functions

スクリーンショット 2025-09-29 15.08.32.png

CloudFront Functions は、エッジでリクエストとレスポンスを操作するためのJavaScriptベースで動作する関数を提供するサービスです。CloudFront Functions を使用して、着信リクエストをインターセプトし、S3 内の正しい HTML ファイルを指すように URI を書き換えることができます。

この問題を解決するための CloudFront Function コードは以下の通りです。

function handler(event) {
  var request = event.request;
  var uri = request.uri;

  // 1. URIがファイル拡張子を含まない場合
  // 例: /about -> /about.html
  // ただし、ルートパス / は除く
  if (uri !== '/' && !uri.includes('.')) {
    request.uri = uri + '.html';
  }

  // 2. URIが "/" で終わる場合、"index.html" を追加
  // 例: /about/ -> /about/index.html
  if (uri.endsWith('/')) {
    request.uri += 'index.html';
  }

  return request;
}

最適化された CloudFront Function は、より汎用的なロジックで Next.js 静的エクスポートのルーティング問題を解決します。

  1. URI がファイル拡張子を含まない場合の .html 追加:

    • if (uri !== '/' && !uri.includes('.')) { request.uri = uri + '.html'; }
    • これは、リクエストされた URI がルートパス (/) ではなく、かつファイル拡張子 (.html, .css, .js など) を含まない場合に適用される。
    • たとえば、/emotion へのリクエストは /emotion.html に書き換えられます。
    • これにより、個々のページパスをハードコードすることなく、Next.js が生成するすべてのページに対応できます。
  2. URI が末尾のスラッシュで終わる場合の index.html 追加:

    • if (uri.endsWith('/')) { request.uri += 'index.html'; }
    • これは、リクエストされた URI が末尾のスラッシュで終わる場合に適用される (例: /about/)
    • URI に index.html を追加し、/about//about/index.html に変換します。これは、Next.js が静的エクスポートでディレクトリ内に index.html を生成する構成に対応する
    • この条件は、前述の .html 追加ルールが適用された後でも有効。例えば、/about//about.html に書き換えられず、そのまま /about.html になることを保証する。

つまり...

  • /path のようなクリーンな URL を /path.html に変換する
  • /path/ のような末尾スラッシュ付きの URL を /path.html に変換する
  • / (ルート) は index.html のままです (CloudFront のデフォルトルートオブジェクト設定によって処理されるか、または2番目の条件で /index.html になります)
  • 画像やCSS、JavaScriptファイルなどの静的アセット (.png, .css, .js など) は、URI に拡張子が含まれるため、この関数によって変更されません。

これにより、CloudFront Functions の設定を簡素化し、Next.js アプリケーションに新しいページを追加するたびに更新する必要がなくなります!

CloudFront Functions のデプロイ方法:

1. 関数の作成:

  • AWS の CloudFront コンソールに移動します。

スクリーンショット 2025-09-29 15.12.45.png

  • 「関数」のページで、新しい関数を作成します。

スクリーンショット 2025-09-29 15.13.28.png

  • 関数の名前を付けます。また「cloudfront-js-2.0」を選択します.
    スクリーンショット 2025-09-29 15.14.05.png

2. 関数の公開:

  • JavaScript コードを関数の本文に貼り付けます。
    スクリーンショット 2025-09-29 15.14.23.png

  • 保存後、関数を発行します。
    スクリーンショット 2025-09-29 15.15.27.png

3. CloudFront ディストリビューションとの関連付け:

  • CloudFront ディストリビューションの設定に移動します。

  • Next.js アプリケーションを配信するビヘイビア (通常は Default ビヘイビア) を編集します。
    スクリーンショット 2025-09-29 15.20.07.png

  • 「関数関連付け」の下で、「ビューワーリクエスト」を選択します。

  • 新しく作成した関数を選択して追加します。
    スクリーンショット 2025-09-29 15.21.40.png

この CloudFront Function を実装することで、S3 および CloudFront にデプロイされた Next.js アプリケーションはすべてのルートを正しく提供するので、403 Forbidden エラーを解決しましょう!

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?