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)またはオリジンアクセスアイデンティティ(OAI)を設定するやり方もあります。現在はOACが推奨されていますので、そちらのパターンについても別途記事で解説予定です。


目次