Japan AWS Top Engineers アドカレ10日目の記事です!
CloudFront+S3でWebサイト公開はキャッシュも使えて料金も安くて本当に重宝しますよね。
Apache、nginx、IISなど使わずに簡単にサイト公開はできるしOSレイヤーの保守やミドルウェアの保守も要らずインフラエンジニアとしてはかなり助かるマネージドな構成だと思います。
ただ、Apache、Nginxで慣れていると思わぬハマりポイントがありますので今日はその一例と解決方法を記載します。
/dashboard/など特定のディレクトリページが見れない
https://example.com/dashboard/
などwebシステムでログイン後にダッシュボードなど表示させたいときにディレクトリ構造を作ることはよくあるかと思います。
今日は上記のように/dashboard/でURLを入力した際にページが表示されない場合の対処方法を記載します。
構成図、S3の構造
構成はよくあるCloudFront+S3のフロント構成です。
S3の構成は以下のような感じです。
┌─────────────────────────┐
│ S3 bucket │
├─────────────────────────┤
│ 📁 404/ │
│ 📄 404.html │
│ 📁 dashboard/ │
│ └── 📄 index.html │
│ 📁 forgot-password/ │
│ 📄 index.html │
│ 📁 login/ │
│ 📁 signup/ │
└─────────────────────────┘
CloudFront配下のS3はOAI(Origin Access Identity)によりS3バケットへの直接アクセスは禁止にしています。
特定のディレクトリにあるindex.htmlが見えない
本来はhttps://example.com/dashboard/にアクセスするとhttps://example.com/dashboard/index.htmlのページが見えてほしいところです。
ですが標準のCloudFront+S3の構成だとページが表示しません。
ただ、https://example.com/dashboard/index.htmlでアクセスするとページは表示します。
なぜ特定のディレクトリにあるindex.htmlが見えないか
S3はオブジェクトストレージであり、LinuxやWindowsのようなファイルシステムとは異なります。
リクエストされたパスをそのままオブジェクトキーとして探すため、https://example.com/dashboard/ でアクセスすると /dashboard/ というオブジェクトを返そうとします。
しかし /dashboard/ はオブジェクトではなく、キー名に / を含むオブジェクトをコンソールが便宜上グループ化して表示しているだけです。
実際にはフォルダもオブジェクトも存在しないため、403エラーが返されます。
内部的には403をS3が返しますがWebアクセスですとトップページのindex.htmlが表示され、ダッシュボードにアクセスしたけどトップページにアクセスが飛ばされるような動きとなります。
curlで確認するx-cache: Error from cloudfrontと返ってきます。
curl -I https://{cloudfornturl}.net/dashboard/ | grep x-cache
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 9868 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
x-cache: Error from cloudfront
デモサイトですがhttps://example.com/dashboard/にアクセスしてもトップのindex.html(Home)が表示されます。内部的にはエラーレスポンスを返して/index.htmlにフォールバックされています。

https://example.com/dashboard/index.htmlにアクセスするとオブジェクトがありますのでブラウザでも正常に表示がされます。

CloudFront FunctionでURLリライトをする
https://example.com/dashboard/などのアクセスで/dashboard/index.htmlのページを表示させるにはCloudFront Functionを作成し、対象のCloudFrontに関連付けをします。
1.[CloudFront]→[関数]を選択し、Functionsタブで「関数を作成」を選択します。
2.[名前]は任意の名前を設定、[Runtime]は「cloudfront-js-1.0」を選択し、作成します。
3.Functionの画面に遷移しますので[構築]タブ内の関数コードを以下コードに書き換えて「変更を保存」を選択します。
function handler(event) {
var request = event.request;
var uri = request.uri;
// URIが/で終わる場合、index.htmlを追加
if (uri.endsWith('/')) {
request.uri = uri + 'index.html';
}
// URIに拡張子がない場合(ファイルでない場合)、/index.htmlを追加
else if (!uri.includes('.')) {
request.uri = uri + '/index.html';
}
return request;
}
4.作成したFunctionをCloudFrontに関連付けします。
対象のCloudFrontを選択し、[ビヘイビア]タブ内のビヘイビアを選択して「編集」を選択してください。
画面下部にある[関数の関連付け]内で[ビューワーリクエスト]の関数タイプを「CloudFront Functions」に設定し、[関数ARN]に作成したFunctionを選択して「Save changes」を選択してください。
以上で設定完了です。
https://example.com/dashboard/にアクセスするとCloudFront Functionでリライト処理が行われてhttps://example.com/dashboard/index.htmlにアクセスできました。
Apache、Nginxではなぜアクセスできるのか
ApacheやNginxといった従来のWebサーバには、ディレクトリにアクセスした際に自動的に index.html を探して返す機能が標準で備わっています。
例えば、/dashboard/ にアクセスすると、Webサーバが自動的に /dashboard/index.html を探して表示してくれます。これはWebサーバの基本機能として、特別な設定をしなくても動作します。
一方、S3はオブジェクトストレージであり、Webサーバのような機能はありません。
指定されたキー(パス)のオブジェクトを返すだけしかできません。
そのため、S3には自動的に index.html を補完する機能がないため、/dashboard/ というリクエストに対して dashboard/ というキーを探しますが、そのようなオブジェクトは存在せず、結果として403エラーが返ります。
これが、CloudFront Functionsによるリライトが必要な理由です。 CloudFront Functionsを使うことで、Webサーバと同じように /dashboard/ を /dashboard/index.html に自動変換できます。
まとめ
CloudFront+S3でSPAやWebサイトを公開する際、ディレクトリ形式のURL(/dashboard/)でアクセスできない問題は、S3がオブジェクトストレージであり、Webサーバのような自動的なindex.html解決機能がないことが原因です。
CloudFront Functionsを使ってURLリライトを行うことでApache、nignxと同等の動作が実現できます。
- CloudFront Functionの変更後、反映には数分かかる場合があります
- 既存のキャッシュがある場合は、キャッシュの無効化が必要な場合があります






