koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

S3 + CloudFront での静的Webサイト配信への気持ち

AWSで静的なWebサイトを配信したいと考えたらS3 + CloudFrontの組み合わせが向いているのだろう。

ただ、ちょっと思うところはあるのでその気持ちを書く記事。

S3の静的Webサイトホスティング機能

docs.aws.amazon.com

上記の通り、S3には静的Webサイトのホスティング機能がある。

最低限のホスティング機能もあり、パス毎のルートオブジェクトやエラーオブジェクトを指定することもできる。

しかし、致命的に気に入らない部分として以下がある。

ウェブサイトエンドポイント - Amazon Simple Storage Service

Amazon S3 ウェブサイトエンドポイントは HTTPS またはアクセスポイントをサポートしていません。

HTTPのみでインターネットにコンテンツを提供する口が開いてしまうという事実を「生理的に受け付けられない」。これは完全に気持ちの問題だ。

HTTPS を使用する場合は、Amazon CloudFront を使用して Amazon S3 でホストされている静的ウェブサイトを提供できます。

はい。

CloudFrontのデフォルトルートオブジェクト

別にHTTPSを使いたい場合でなくとも、独自ドメインで配信したいとなったらCloudFrontを挟むことになる。

ところが、CloudFrontはオリジンが完全な配信を行ってくれることを期待しているため、ルーティングの機能はWebサイトの配信に対しては貧弱だ。

デフォルトのルートオブジェクトの指定 - Amazon CloudFront

デフォルトルートオブジェクトを定義しても、ディストリビューションのサブディレクトリに対するエンドユーザーリクエストはデフォルトルートオブジェクトを返しません。例えば、index.html がデフォルトルートオブジェクトであり、CloudFront が CloudFront ディストリビューション下の install ディレクトリに対するエンドユーザーリクエストを受け取ったと仮定します。

https://d111111abcdef8.cloudfront.net/install/

index.html のコピーが install ディレクトリ内にあっても、CloudFront はデフォルトルートオブジェクトを返しません。

これは昨今のフロントエンドフレームワークのSSGと大層食べ合わせが悪い。

S3とCloudFrontの組み合わせ

S3に/has-index-document/index.htmlというキーのオブジェクトが置いてある状態で、CloudFrontのオリジンとしてS3バケットを指定した場合こうなる。

同様にオリジンにS3の静的Webサイトホスティングドメインを指定した場合はこうなる。この動きを期待したい。

これらはいずれもS3の静的Webサイト機能がいい感じに解決してくれる。

が、HTTPでコンテンツが配信されてしまう。

http://static-web-hosting-patterns-aws-cloudfront-s3-static-web.s3-website-ap-northeast-1.amazonaws.com/

これを「生理的に受け付けられない」という話。

参考

じゃぁどうするのか?

とりあえず「CloudFrontのオリジンとしてS3バケットを指定」して、index.htmlに解決されたいキーにindex.htmlをコピーすることを試している。

通常のファイルシステムの上では「/sub_dir」ディレクトリと「/sub_dir」ファイルは両立しないが、S3では「/sub_dir」というオブジェクトのみになるので成立する。

GitHub Actionsのワークフローでこんな感じ。

      - run: aws s3 sync ./out s3://XXXX
      - name: Fix route
        run: |
          cd ./out && find ./ -type f -name '*.html' | while read HTMLFILE; do
            BASENAME=${HTMLFILE#*/};
            FILENAME=${BASENAME%.*};
            if [[ "$FILENAME" != "index" ]];
            then
              aws s3 cp s3://XXXX/${BASENAME} s3://XXXX/${FILENAME};
            fi
          done
      - run: aws cloudfront create-invalidation --distribution-id XXXX--paths "/*"

オブジェクト数が多いとコピーに結構時間がかかるのでイマイチ感は強い。

もう少しよい付き合い方が見つかったらまた記事にしたいと思う。

S3の静的Webサイト機能がHTTPSサポートが嬉しいのだけれどなぁ?

そもそも、CloudFrontにしてもHTTPとHTTPSで料金が違ったりするのが現代的でないというか、そこそこの不満がある構成である。

そんな感じ。