AWS S3にアップロードした静的サイトを独自ドメインでCloudFrontから配信する


前記事:静的サイトをGithub ActionsでAWS S3にデプロイする

AWS S3にアップロードした静的サイトをCloudFrontを通して配信します。CloudFrontを使わずS3から配信する方法もあるようですが、独自ドメインやSSL、配信時のgzipなど使いたいのでCloudFrontを使います(これらS3だけで完結可能かもしれませんが調べきれていません)

すでに独自ドメインは取得済みとして進めます、このサイトのドメインはGoogle Domainsで取得しているので設定はGoogle Domainsを使います。また、独自ドメインがなくてもcloudFrontが生成したURLでサイト配信も可能です。

S3のアクセス権限をパブリックにする

CloudFrontはルート階層のindex.htmlは読み取ってくれますがサブディレクトリのindex.htmlには対応してくれません。例えばこの記事は下記の階層にあります。

https://crudzoo.com/blog/aws-s3-cloudfront/index.html

CloudFrontだけだとURL直接や検索エンジンからアクセスすると、https://crudzoo.com/index.html にリダイレクトします。サイトに入ってからリンクを辿るとたどり着けますがURLからアクセスできません。

そういったルーティング機能はS3の静的ホスティング機能を使って補うことができます。ただし、S3をパブリック公開する必要があります。

S3の設定

静的サイトをアップロードしたS3 バケットのプロパティでStatic website hostingをONにします。次にアクセス権限タブから下記の権限を一時的にオフにします。

  • 新規のパブリックバケットポリシーまたはアクセスポイントポリシーを介して付与されたバケットとオブジェクトへのパブリックアクセスをブロックする
  • 任意のパブリックバケットポリシーまたはアクセスポイントポリシーを介したバケットとオブジェクトへのパブリックアクセスとクロスアカウントアクセスをブロックする

バケットポリシーにアクセス権限を追加します

  {
   "Sid": "3",
   "Effect": "Allow",
   "Principal": "*",
   "Action": "s3:GetObject",
   "Resource": "バケット名/*"
  }

最後にブロックパブリックアクセスで下記だけ許可するように変更します

  • 任意のパブリックバケットポリシーまたはアクセスポイントポリシーを介したバケットとオブジェクトへのパブリックアクセスとクロスアカウントアクセスをブロックする

余談ですがこのあたりの仕様把握できていなかったので、このサイトは1週間くらい全ページがトップページにリダイレクトする不具合を起こしていました。

CloudFrontの作成

まずは独自ドメインを当てずにAWS CloudFrontからウェブサイトを配信します。独自ドメインを当ててから配信したい場合はこの手順をスキップしてください。また、途中設定を失敗しても編集でやり直しできますので、適当に作ってみて問題があれば設定を見直すのも良いです。

"CloudFront"

delivery methodはwebで進めます。Origin Settingsでは静的サイトをアップロードしたS3のStatic website hostingのところにあるエンドポイントを指定します。

"CloudFrontの設定"

Default Cache Behavior SettingsではDefault Root ObjectCompress Objects Automaticallyの2箇所を変更します。

  • Default Root Object: index.html
  • Compress Objects Automatically: Yes

Compress Objects Automaticallyは必須ではありませんが、ReactやGatsbyなどJavaScriptのライブラリで作成したウェブサイトは、Compress Objects AutomaticallyはYesにした方が良いです。

CreateReactAppでbuildした時、terminalに実ファイル容量とgzip配信時の容量が表示されていると思います。配信時のファイルサイズが全然違い、lighthouseの評価もグッと変わりますので重要な設定となります。AWS関係なく、JavaScriptメインのサイトはgzipが有効になっているか確認しましょう。

他にもhttp配信が必要ない場合はredirect to httpsに設定し、作成を押します。CloudFrontは作成までに数分かかり、作成されるとS3のファイルにURLからアクセス可能となります。

Error Pagesの設定

実ファイルがなくJavaScriptでルーティングしているケースでは、ファイルがない時にindex.htmlにリダイレクトする必要があります。先ほど作成したCloufFrontをクリックし、Error Pagesタブにいきます。

"CloudFrontの設定"

403と404を作成しておきます。

配信の確認

ここまでの作業ですでにウェブサイトは確認できる状態です。CloudFrontのディストリビューションに表示されているDomain Name(xxxxxxx.cloudfront.net)でS3にアップロードした静的ファイルが表示されるはずです。ここでAccess Denyがでた場合は、S3との設定が上手くいっていないので再度CloudFrontの設定を見直してください。もしくは、Default Root Objectが設定されていない、Error Pagesの設定がおかしいかもしれません。そもそもS3にあるファイルがおかしいかもしれません。

キャッシュの削除

CloudFrontは常に最新のS3の状況を反映しているわけではなく、1度反映されたら24時間同じ内容をキャッシュします。キャッシュをクリアしたい時は編集からInvalidationsで、/*を行います。

これはCloudFrontのキャッシュを全て消すだけでS3には影響がありません。Invalidationsは1000回まで無料ですがそれ以降は有料です。S3にDeployした時にCloudFrontのキャッシュも消す場合、頻繁にデプロイする場合はコストに注意してください。

独自ドメインで配信する

ここまでの作業でGithubにコードプッシュ〜サイト配信までのCI/CDは完了となります。次はCloudFrontから独自ドメインで配信する設定を行います。まず、独自ドメインはどこかで取得済みとします。これから取得するならAWSのRoute53で取得するのが面倒がないです。

大まかな手順は下記です。

  • Route53でホストゾーン作成 (DNS設定のため、0.5$かかります)
  • ドメイン用のSSLをAWS Certificate Managerで取得
  • CloudFrontに設定

ルートドメインではなくサブドメインからのみ配信する場合はRoute53なしでも可能です(例えばcrudzoo.comではサイト配信しないけどwww.crudzoo.comでは配信する)。もしくはGoogle Domainsから転送かける方式でも解決したかもしれません。

サイト移転時の注意

すでに運営しているドメインでの移行を考えている場合、DNSの切り替えには半日以上かかりますので注意が必要です(最大48時間)。これを忘れて元の設定を消し去って作業すると、しばらくサイトにアクセスできなくなる状態になります。良い移行方法を知っているわけではないのでノウハウは共有できません。

わたしはそのことをすっかり忘れて移行作業したので、このサイトは1日くらい死んでました。個人サイトなので笑い話です。

Route53でホストゾーンを作成

ルートドメインでの配信を行うならまずはRoute53でホストゾーンを作成します。ドメイン自体は他のサービスで取得していても大丈夫です。ただし、そのドメインを使用する権限を持っていないとダメです。

ホストゾーンが作成できたら、ネームサーバー(NS)4つとSOAのレコードが作成されているはずです。次にAとAAAAのレコードを作成します。レコードを追加を選択し、シンプルルーティングを選択。

シンプルなレコードを定義で、レコード名は空欄にします。エンドポイントに先ほど作成したcloudfrontディストリビューションが選択できるはずなのでそれを選択してください。

www.ドメイン名も必要なら同様にAで作成が必要です。

SSLの作成

AWS Certificate Managerにアクセスし、メニューバー上部のリージョンを米国東部(バージニア北部)に変更してください。CloudFrontが対応しているリージョンで取得する必要があります。

https://docs.aws.amazon.com/ja_jp/Route53/latest/DeveloperGuide/routing-to-cloudfront-distribution.html

AWS Certificate Managerで証明書のリクエストを行います。Route53でホストゾーンを設定していればスムーズに進めると思います。Route53を使用していない場合は、ドメイン取得もとでカスタムレコードを設定するかEmailで認証を受ける必要があります。

"acm"

こんな感じです。

ドメイン取得元でネームサーバーの変更

Route53以外でドメインを取得していた場合は、取得元でDNSの変更を行います。Google Domainsなら反映するドメインのDNSタブにネームサーバー設定があります。

"google domains"

Route53でホストゾーン作成時に4つのネームサーバーが表示されていますのでそれらを設定します。語尾の.は省きます(Google Domainsだと自動で省かれましたが)。

CloudFrontに設定を反映

CloudFrontに戻り、ディストリビューションのGeneralタブでEditを押します。

  • Alternate Domain Names(CNAMEs):設定するドメイン名をいれる
  • SSL Certificate: Custom SSL CertificateでCertificate Managerで作成したものを選択
  • Custom SSL Client Support: recommendedがついてる方
  • Security Policy: recommendedがついてるもの

他はそのままで大丈夫です。SSL Certificateが選べない場合、Certificate Managerでの証明書作成が失敗しているか、リージョンが間違っている可能性があります。

"acm2"

Certificate Managerで該当のドメインの証明書が発行済みになっているかを確認してください。

DNSの確認

ここまでで設定は終了です。あとは反映に時間がかかりますので失敗してないことを祈りましょう。念のため、祈りが届くかどうかをterminalから確認しておきます。

dig ドメイン名 設定したネームサーバー

dig example.com xxx.xx.x.uk みたいな感じです。

"answer"

ANSWERが1なら大丈夫です、ウェブサイトに独自ドメインでアクセスできるまでは時間の問題となります。ANSWERが0だとどこか失敗している可能性が高いです。

終わりに

サイト移行作業はかなり苦戦し、途中で辞めたくなりましたが連休パワーで乗り切りました。Github Actionsとは別にCodePipelineでも同様に配信までやってみましたが、これくらいの簡単なフローならGithub Actionsの方が手間がないかなと思いました。

途中長いダウンタイム作ってしまったのが反省点です。あとはGithub ActionsにCloudFrontのキャッシュ消すタスクを追加するのが次の連休の宿題です。

最後にLighthouseの結果です。

Netlifyの時

"lighthouse"

S3 + CloudFront

"lighthouse"

Performanceが上がってSEOが下がる。ついでにサイト自体のPerformanceを改善しまして

サイトのパフォーマンス調整

"lighthouse"

こうなりました。改善時にCSS吹き飛ばしすぎて記事中のコードハイライトなどいくつか機能が動いていない不具合がでています。ナニヲシテイルンダ

しばらくAWS課金の様子見をしつつ、サイト自体の改善に戻ります。