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バケットでウェブサイトのホスティングを有効化します。

インデックスドキュメントに以下に設定しました。
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(後から追加されて推奨されている)のパターンでやってみた記事を書きたいと思います。

目次