AWS
S3
CloudFront
Lambda@Edge

CloudFront + S3 でのディレクトリアクセス

More than 1 year has passed since last update.

前置き

S3にアップロードしたコンテンツをウェブ公開するときに、CloudFrontを噛ませるのはよくある構成です。S3をStatic Website Hostingで直接公開する場合とくらべて、

  • CDNの機能が使える
  • WAFが使える(CloudFrontにWAFをアタッチした場合)
  • 独自ドメインのhttps構成が使える(CloudFrontにssl証明書を付与)
  • アクセスはCloudFront経由に集約されるため、CloudFrontのアクセスログでアクセス状況を分析できる

といったメリットがあります。しかし、Static Website Hosting で使える index document が使えなくなるという問題が。

S3のindex documentとは

たとえば http://hoge.jp/fuga/ というふうにディレクトリに対するリクエストがきたときにどのファイルをレスポンスするか、を設定できる機能です。
公式ドキュメント

CloudFrontにもDefault Root Objectという似た機能がありますが、これはルートディレクトリに対するリクエストにのみ有効で、サブディレクトリへのアクセスには機能しません。

解決策として手っ取り早いのは、オリジンのS3のStatic Website Hostingを有効にすることです。
参考ページ

が、「S3バケットは直接パブリック公開したくない!」という場合にどうするか。

案1 Lambda@Edge

  1. CloudFrontにディレクトリアクセス用のBehaviorsを追加。 既存Behaviorsと同じS3をオリジンに。PathPatternは /*/とすることで、サブディレクトリへのアクセスがこのBehaviorsに割り当てられます。既存Behaviorsよりも順位を上げておくのを忘れないように。
  2. 追加したBehaviorsにLambda@Edgeをアタッチ。
index.js
'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    request.uri = request.uri.replace(/\/$/i, '/index.html');
    callback(null, request);
};

サブディレクトリ(/*/)へのリクエストはLambda@Edgeによって/*/index.htmlへのリクエストに置き換えられます。
手順1は必須ではなく、既存のBehaviorsにLambda@Edgeをアタッチしてもいいんですが、その場合は全リクエストに対してLambda@EdgeがCallされてしまうので、ちょっと無駄かなと思います。ルートディレクトリへのアクセスはCloudFrontのDefault Root Objectの設定でカバーします。

案2 ダミーのS3バケットからリダイレクト

Lambda@Edgeがリリースされるまではこの案が第一候補だったかもしれませんが、今となってはLambda@Edge案一択でいいかも…。Lambda@Edgeのコストが許容できない場合や、ディレクトリ構成が単純な場合は引き続きこの案も有効かと思います。
1. 空っぽのダミーS3バケットを作成。このバケットはStatic Website Hostingの設定を有効化。
2. CloudFrontにディレクトリアクセス用のBehaviorsを追加。ダミーバケットをCloudFrontのオリジンに。PathPatternは /*/
3. ダミーバケットにリダイレクトルールを設定。リダイレクトルールは正規表現が使えないので、リダイレクトさせたいサブディレクトリ名をすべて指定する必要があります。
なお、この案はダミーで空っぽとはいえs3のパブリック公開が必要です。