CloudFrontのデフォルトルートオブジェクト設定に対する私的まとめ

2022.07.12

しばたです。

CloudFrontではデフォルトルートオブジェクトを設定することができます。
デフォルトルートオブジェクトは、例えばhttps://example.com/にアクセスした際にhttps://example.com/index.htmlの内容を返してくれるアレ(index.htmlをデフォルトオブジェクトにするアレ)のことです。

この機能についてはAWSのドキュメントやDevelopersIOでも結構触れられているのですが、私が仕様をずっと誤解してたので本記事で恥をさらすとともに注意点を解説したいと思います。

各種ドキュメント

本記事で参考にしているドキュメントやブログ記事を最初に紹介しておきます。

1. CloudFrontの機能では「ルートディレクトリ」のみ対象である

AWSのドキュメントにもある通りCloudFrontのデフォルトルートオブジェクトはルートディレクトリのみ有効です。

例えばindex.htmlをデフォルトルートオブジェクトに設定した場合、ルートディレクトリであるhttps://example.com/にアクセスした場合は

  • https://example.com/index.html

の内容を返してくれますが、サブディレクトリであるhttps://example.com/hoge/は対象外で

  • https://example.com/hoge/

の内容がそのまま返されます。
(具体的にどの様な結果になるかは環境依存)

このため、サブディレクトリでもデフォルトオブジェクトを設定したい場合はこちらの記事にある様にLambda@Edgeで頑張る必要があります。

URLの差し替えをしてるだけなので今ならLambda@EdgeよりCloudFront Functionsを使うほうが良いかもしれません。(未検証)
以下の記事にCloudFront Functionsの実装例がありましたで紹介しておきます。

例1. S3バケットをオリジンにした場合 (S3 Origin)

この挙動が問題となり得るのはCloudFrontのオリジンにS3バケットを指定した場合(S3 Origin)だと思います。

私の検証用AWSアカウントに以下の環境を用意して実際の動作を確認したいと思います。

  • コンテンツを配備するS3バケットをひとつ用意
    • バケット名はcf-root-object-test-20220712で東京リージョンに作成
    • ACL無効、パブリックアクセスブロックにしコンテンツは全てPrivateに
    • /index.html/hoge/index.htmlの2つのオブジェクトを配置

S3はざっくりこんな感じです。

/index.html

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Root Index HTML</title>
</head>
<body>
Hello Root Index HTML
</body>
</html>

/hoge/index.html

<!doctype html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Hoge Index HTML</title>
</head>
<body>
Hello Hoge Hoge
</body>
</html>
  • CloudFrontディストリビューションを1つ用意する
    • 代替ドメイン名を example.shibata.tech とし証明書やDNSの設定を適切に準備しておく
    • オリジンをS3バケット cf-root-object-test-20220712 にしOAIとバケットポリシーはよしなに設定
    • ビヘイビアはデフォルトの (*) だけとし、常にオリジンにアクセスする様に設定

オリジン設定はこんな感じ。

まだデフォルトルートオブジェクトは未設定です。
この状態でhttp://example.shibata.tech/にアクセスするとアクセスエラーになり、

http://example.shibata.tech/index.htmlにアクセスするとコンテンツを表示してくれます。

次にCloudFrontディストリビューションの「一般」設定からデフォルトルートオブジェクトをindex.htmlに設定します。

しばらく待って変更が反映された後でhttp://example.shibata.tech/にアクセスするとindex.htmlの内容を表示してくれます。

ただ、最初に述べた通りサブディレクトリhttp://example.shibata.tech/hoge/についてはこの設定は反映されません。

サブディレクトリについてはhttp://example.shibata.tech/hoge/index.htmlとオブジェクトを明示する必要があります。

なお、今回はアクセスエラーになりましたがバケットポリシーの設定の仕方によってはディレクトリ指定をすると

Content-Type: application/x-directory;

となり空ファイルのダウンロードをしてしまうことがあります。
いずれにせよサブディレクトリではデフォルトオブジェクトを設定できません。

2. オリジンで同等の機能がある場合は併用できる

次に、ApacheやNginxといった一般的なWEBサーバーにも同等の機能 *1がありCloudFrontのデフォルトルートオブジェクトとは別個に扱うことができます。

例えばEC2上のApacheをオリジンにしてDirectoryIndexディレクティブを設定にした場合は次の評価順となります。

  • ルートディレクトリ (/) の場合
    1. CloudFrontのデフォルトルートオブジェクトが設定されている場合、CloudFrontの設定が優先されApache側設定は無視される
    2. CloudFrontのデフォルトルートオブジェクトが設定されていない場合、ApacheのDirectoryIndexディレクティブ設定が有効になる
  • サブディレクトリ (/hoge/ など)の場合
    1. 常にApacheのDirectoryIndexディレクティブ設定が有効になる

例2. S3静的ウェブサイトをオリジンにした場合 (Custom Origin)

ここで敢えてややこしい例を出し、S3静的ウェブサイトをオリジンにして動作確認していきます。

S3には単体でHTTPのサイトを公開できる「静的ウェブサイトホスティング」の機能があります。
この機能を使って作成したウェブサイトはCloudFrontから見ればただの外部にあるWEBサーバーでありEC2オリジンと同等の扱いとなります。

CloudFrontとS3を使って静的なサイトを作る際に「S3バケットをオリジンにする」のと「S3静的ウェブサイトをオリジンにする」のは全く別なのでご注意ください。
私はずっと両者をごちゃまぜにして認識してました...

そしてS3静的ウェブサイトには「インデックスドキュメント設定」があり、こちらはサブディレクトリに対してもデフォルトオブジェクトを指定することができるので今回この機能を有効にします。

ここから動作確認を開始します。
まずは、前節で使ったS3バケットcf-root-object-test-20220712の設定を変え静的ウェブサイトとして公開します。

  • 静的ウェブサイトホスティングを有効にし、インデックスドキュメントにindex.htmlを設定
  • 静的ウェブサイトホスティングでは各オブジェクトにPublicにアクセスする必要があるためバケットポリシーを更新し全公開に

次に、CloudFrontのオリジン設定をS3バケットではなく、公開されたサイトのURLであるhttps://cf-root-object-test-20220712.s3-website-ap-northeast-1.amazonaws.comにします。
(同時にビヘイビアも変えておきます)

これで前節の例と違いオリジンタイプが「S3」ではなく「Custom Origin」となり、ドメイン指定も「s3-website-ap-northeast-1」の含まれる別URLになっているのが分かります。
この場合はEC2オリジンと同様であり、インデックスドキュメント設定により各ディレクトリでデフォルトオブジェクトが表示されます。

こんな感じでS3静的ウェブサイトであればLambda@Edgeを使うより容易にデフォルトオブジェクトを表示できますが、S3内部のオブジェクトをPublicにする必要があり、かつHTTPのサイトが必ず公開される形になるので一長一短といった感じです。

最後に

以上となります。

「S3バケットをオリジンにする」と「S3静的ウェブサイトをオリジンにする」の違いをちゃんと理解していれば挙動の違いも理解しやすいと思います。

本記事の内容が皆さんの役に立てば幸いです。

脚注

  1. "DirectoryIndex"ディレクティブや"index"ディレクティブなど