ReactアプリをAWS S3 + Cloudfrontで静的ホスティングの環境構築
ReactアプリをAWS S3 + Cloudfrontの構成にデプロイするときの設定について書きたいと思います。色々なデプロイ先があるかと思いますが、最もお手軽な方法の一つとして、試してみる価値があるのではないかと思います。
また、Web上にナレッジは多いのですが、最新のS3、Cloudfrontで環境を作るにあたっては少し詰まるところがありました。またReact Routerなどで動的ルーティングしたときに、直接URLにアクセスされた場合や存在しないリソースにアクセスされた時の対応をしています。地味ですがアプリ公開にあたっては重要ポイントですので、参考になればと思います。
S3バケット設定
CloudFrontとS3で静的ホスティングする方法は数パターンあります。今回はその中のS3の静的ウェブサイトを使う方法を説明
S3バケットの設定
- バケットの作成
-
私は以下の名前でバケットを作成しました。自身のアプリ名などに合わせて名前を設定してください。
knowledge-react-aws-s3-cloudfront-hosting
- Webホスティングを有効化
-
ドキュメントに従い、S3バケットでウェブサイトのホスティングを有効化します。
ウェブサイトのホスティングの有効化 – Amazon Simple Storage Service Amazon S3 コンソールを通じてウェブサイトのホスティングを有効にします。インデックスドキュメントに以下に設定しました。
index.html
- アクセスに制限する
-
S3のウェブサイトエンドポイントを利用するときのシンプルなアクセス制限としてHTTPリファラーを使う方法があります。リファラーはHTTPヘッダーの一つで、一般には遷移元のURLが設定されており、どこからアクセスが来たのかを知ることができます。
今回は、バケットポリシーを使い任意のランダム値がリファラーに設定されているときのみにアクセスを許可します。後でCloudFrontからS3へのアクセスにリファラーを追加するように設定します。{ "Version": "2012-10-17", "Statement": [ { "Sid": "PublicReadGetObject", "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::knowledge-react-aws-s3-cloudfront-hosting/*", "Condition": { "StringEquals": { "aws:Referer": "{任意のランダム値}" } } } ] }
- CORS設定
-
別のオリジンへのアクセスを許可するためにCORS設定をします。以下のようにCross-Origin Resource Sharing (CORS)の設定をしました。こちらも各々の必要に応じて設定はカスタマイズすると良いと思います。
[ { "AllowedHeaders": [ "*" ], "AllowedMethods": [ "GET" ], "AllowedOrigins": [ "*" ], "ExposeHeaders": [], "MaxAgeSeconds": 3000 } ]
補足:Webサイトエンドポイント
静的ウェブサイトを有効にすると、S3はWebブラウザを通したアクセスに対して最適化されます。具体的にはREST APIエンドポイントからWebサイトエンドポイントに切り変わります。そして今回の設定ではCloudFrontがWebサイトエンドポイントを呼び出すような構成になります。直接は呼び出しませんが、この仕組みとREST APIエンドポイントとの違いを理解しておくと何を設定しているのかがイメージしやすくなります。
CloudFrontの設定
ディストリビューションの作成
基本は以下に従ってディストリビューションを作成していきます。少し変更点がありますので、後ほど説明します。
以下が変更点です。
- オリジン設定
-
先ほど作成したバケットを設定します。ウェブホスティングが有効になっていると、以下のようにウェブサイトエンドポイントによるアクセスを有効化できます。
- リファラー設定
-
S3バケットのバケットポリシーで設定したランダム値をここで設定します。
Referer
をキーにして設定してください。
エラーページの設定
S3に存在しないリソースに直接アクセスが来たら、インデックスドキュメントを返します。フロントエンドの実装にはよりますが、以下の動作が実現できるかと思います。あとはアプリに合わせてページ遷移を調整していけばOKです。
- 存在するパスであれば、ページが再読み込みされる。
- 存在しないパスであれば、ルートにマッピングされたページが表示される。
デプロイ
最後に動作確認のためにデプロイをしてみます。繰り返しデプロイをするためにスクリプトを用意しました。本筋から外れて若干蛇足になりますが、何らかの方法で自動化しておくと便利です。
#!/bin/bash
AWS_PROFILE={AWSプロファイル}
DISTRIBUTION_ID={ディストリビューションID}
BUCKET_NAME=knowledge-react-aws-s3-cloudfront-hosting
# ビルド
yarn build
# ビルド出力をS3にコピー
aws s3 cp ./dist "s3://${BUCKET_NAME}" --recursive --profile "${AWS_PROFILE}"
# CloudFrontのキャッシュを削除
aws cloudfront create-invalidation --distribution-id "${DISTRIBUTION_ID}" --paths "/*" --profile "${AWS_PROFILE}"
終わりに
S3 + CloudFrontで環境構築を通して、S3のWebサイトエンドポイントを有効にする方法を説明しました。Reactプロジェクトを手軽にデプロイしたいとなったときに選択肢の一つになれば幸いです。
また、別の方法としてS3のREST APIを使い、アクセス制限にオリジンアクセスコントロール (OAC) or オリジンアクセスアイデンティティ (OAI)を設定するというやり方があります。今度、構築する際はREST APIとOAC(後から追加されて推奨されている)のパターンでやってみた記事を書きたいと思います。