Next.js静的サイトとCloudFront OAC構成でページリロード時に「AccessDenied」になる原因と解決策
Next.js静的サイトとCloudFront OAC構成でページリロード時に「AccessDenied」になる原因と解決策
![]()
Next.jsプロジェクトを静的プレレンダリング (output: 'export') して、AWS S3とCloudFront環境へデプロイする際、セキュリティベストプラクティスとして「S3の静的ウェブサイトホスティングを無効化」したまま、OAC (Origin Access Control) を用いてCloudFront経由に限定する構成が標準的です。
しかし、この構成においては一見問題なく動いているように見えても、ある致命的な落とし穴が存在します。
発生する問題の症状
トップページ (/) にアクセスし、Next.jsの内部リンク (<Link>コンポーネント) を通じて記事ページ (/tech_blog など) へ遷移する場合は、正常にページが表示されます。
しかし、記事ページでブラウザのリロード(F5)を押したり、記事ページのURLを直接ブラウザに入力してアクセスしようとすると、以下のようなエラー画面になりページを開けません。

<Error>
<Code>AccessDenied</Code>
<Message>Access Denied</Message>
</Error>
なぜこのエラーが発生するのか?
原因は、Next.jsのファイル出力形式と、CloudFront + S3(REST URL通信)のディレクトリ解決の仕様の違いにあります。
Next.jsの出力仕様 (trailingSlash: true)
Next.jsの next.config.ts で trailingSlash: true を設定した場合、/tech_blog というルートはビルド時に /tech_blog/index.html というパスの実体ファイルとしてS3にエクスポートされます。
S3の挙動
CloudFront経由で「S3のRESTエンドポイント」へアクセスすると、S3は要求されたキー(URLのパス)に対して完全に一致するファイルを返そうとします。S3の「静的ウェブサイトホスティング」機能であれば、自動的に末尾のパスに /index.html を補完してくれますが、RESTエンドポイント(OAC構成時)は、この機能を持ちません。
そのため、URL https://zaregaki.net/tech_blog/ に直接アクセスした場合、S3には tech_blog/ という名前のディレクトリ実体は存在しないため「ファイルが見つからない(404 Not Found)」状態となり、S3の公開アクセス権限設定によりそれが 403 AccessDenied に変換されて表示されてしまいます。
解決策: CloudFront FunctionsでURLを書き換える
S3を非公開(OAC構成)のまま維持する場合、S3へリクエストが届く前に、CloudFront側でURLの末尾に index.html を自動付与する必要があります。そのための軽量で最適な解決策がCloudFront Functionsです。
手順
-
CloudFront関数の作成 AWS管理コンソールの CloudFront メニューから「関数 (Functions)」を開き、「関数の作成」をクリックします。名前に「
RewriteIndexHtml」等をつけます。 -
関数のコードを入力 以下のコードをエディタに貼り付けて保存し、「関数を発行(Publish)」します。
function handler(event) {
var request = event.request;
var uri = request.uri;
// パスが '/' で終わる場合は 'index.html' を付与 (例: /tech_blog/ -> /tech_blog/index.html)
if (uri.endsWith('/')) {
request.uri += 'index.html';
}
// パスに拡張子が含まれていない場合は '/index.html' を付与 (例: /tech_blog -> /tech_blog/index.html)
else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}

- ディストリビューションに「ビヘイビア」として割り当て 対象のCloudFrontディストリビューションにて、「ビヘイビア (Behaviors)」タブからデフォルトのビヘイビアを編集します。下部にある「関数の関連付け (Function associations)」を開き、以下の通り設定します。
- イベントタイプ (Event type):
ビューアクリクエスト (Viewer Request) - 関数タイプ (Function type):
CloudFront Functions - 関数 ARN / 名前:先ほど作成した関数名を選択
この設定を保存してCloudFrontに反映(デプロイ)されると完了です。
まとめ
Next.jsでのSSGアプリケーションとAWS(CloudFront/S3)の組み合わせは非常にスケーラブルですが、ディレクトリの振る舞いにおいて躓きやすいポイントがあります。OAC構成を採用する際は、CloudFront FunctionsによるURLリライトの設定をセットで行うことを強く推奨します。
ところで、今回は生成AIに関連する画像を生成してもらったのですが、すごいですね!
![]()
関連記事 その1: AWSの静的サイト配信で必ず出会う「OAI」と「OAC」の違いを、初心者向けにわかりやすく調査・解説します。なぜ新機能のOACへの移行が推奨されているのか?