AWSで静的なWebサイトを配信したいと考えたらS3 + CloudFrontの組み合わせが向いているのだろう。
ただ、ちょっと思うところはあるのでその気持ちを書く記事。
S3の静的Webサイトホスティング機能
上記の通り、S3には静的Webサイトのホスティング機能がある。
最低限のホスティング機能もあり、パス毎のルートオブジェクトやエラーオブジェクトを指定することもできる。
しかし、致命的に気に入らない部分として以下がある。
ウェブサイトエンドポイント - Amazon Simple Storage Service
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バケットを指定した場合こうなる。
- https://d6v5w007h85or.cloudfront.net/
- デフォルトルートオブジェクトが効くので
/index.html
が表示される
- デフォルトルートオブジェクトが効くので
- https://d6v5w007h85or.cloudfront.net/has-index-document/
- 解決されず、エラーする
- https://d6v5w007h85or.cloudfront.net/has-index-document/index.html
- 存在するオブジェクトへのアクセスなので
/has-index-document/index.html
が表示される
- 存在するオブジェクトへのアクセスなので
同様にオリジンにS3の静的Webサイトホスティングのドメインを指定した場合はこうなる。この動きを期待したい。
- https://d33jpklabwd7er.cloudfront.net/
- https://d33jpklabwd7er.cloudfront.net/has-index-document/
- https://d33jpklabwd7er.cloudfront.net/has-index-document/index.html
これらはいずれもS3の静的Webサイト機能がいい感じに解決してくれる。
が、HTTPでコンテンツが配信されてしまう。
これを「生理的に受け付けられない」という話。
参考
じゃぁどうするのか?
とりあえず「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で料金が違ったりするのが現代的でないというか、そこそこの不満がある構成である。
そんな感じ。