背景
この間、運営している2つのメディアサイトを統合させたくて、AWSのCloudFrontで配信している古いサイトから外部のサイトに移行する必要があった。
-
old.example.com
はCloudFrontを使ってS3に入っているコンテンツを配信しているサイト -
new.example.com
はAWS外でホスティングしているサイト
古いサイトのページはアクセス数が少ないため、内容は移行せずold.example.com/*
に来たアクセスを一律new.example.com/home
にリダイレクトするのが今回の要件。
なお、AWSのリソースや設定はTerraformで管理している。
やったことその① HostName
とReplaceKeyWith
でURLを完全に書き換える
まずはリダイレクトの基本設定として、RoutingRules
にRedirectRule
を追加する。
ReplaceKeyWith
を使ってすべてのリクエストを同じURLにリダイレクトする。そして、今回リダイレクトしたいURLはドメインも違うのでHostName
でリダイレクト先のドメインになる。(外部のドメインになるということは特に意識する必要はない。)
website {
routing_rules = jsonencode([
{
Redirect = {
HostName = "new.example.com"
HttpRedirectCode = "301"
Protocol = "https"
ReplaceKeyWith = "home"
}
}
])
}
こうするとドメインもそれ以降のURL(CloudFormationでS3オブジェクトのキーとして使われていた部分)も置き換えられる。
やったことその② IndexDocument
を追加する
すべてのリクエストをリダイレクトしているからIndexDocument
はいらないと思っていたけど、 terraform apply
をしたら以下のエラーが発生した。
Error: Must specify either index_document or redirect_all_requests_to.
どうやらTerraformの使用上、redirect_all_requests_to
を使っていない限り、IndexDocument
の設定が必須。
website {
index_document = "home"
routing_rules = jsonencode([
{
Redirect = {
HostName = "new.example.com"
HttpRedirectCode = "301"
Protocol = "https"
ReplaceKeyWith = "home"
}
}
])
}
ページを含めて移行していたら、redirect_all_requests_to
ですべてのリクエストのドメインだけを変えてリダイレクトを設定できるけど、どのリクエストでも固定したページにリダイレクトしたいのでIndexDocument
として現在の「Default Root Object」のキーを設定することにした。
やったことその③ CloudFrontのOriginを変更する
上記のリダイレクト設定は正しいが、そもそもリダイレクトを使うためにCloudFrontが使っているS3 BucketのURLを変更する必要があるのが分かった。
NG: old-example-files.s3.amazonaws.com
OK: old-example-files.s3-website.ap-northeast-1.amazonaws.com
(CloudFrontのOrigin設定なのでブラウザに入力するURLと関係ない)
origin {
- domain_name = aws_s3_bucket.old_example_files.bucket_domain_name
+ domain_name = aws_s3_bucket.old_example_files.website_endpoint
origin_id = "S3-old-example-files"
}
Terraformの場合、aws_s3_bucket
のリソースにwebsite_endpoint
という属性が使える。
やったことその④ Custom Origin Configを追加する
リソースの変更は成功したけど、ブラウザで古いサイトのページにアクセスしてみるとそもそも開かない…
When we want to create a CloudFront origin for a S3 static website, we must define a custom_origin_config, otherwise it won't work.
Custom Origin Configの設定も必要か。
origin {
domain_name = aws_s3_bucket.old_example_files.website_endpoint
origin_id = "S3-old-example-files"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
やったことその⑤ 使っているAWSリージョンの正しいウェブサイトエンドポイントを使うようにする
AWSリージョンによってURLが違うよ、とAWSのドキュメンテーションに書いてあったからTerrraformがよしなにやってくれると思いきや、そうでもなかった…
NG: old-example-files.s3-website-ap-northeast-1.amazonaws.com
OK: old-example-files.s3-website.ap-northeast-1.amazonaws.com
どのリージョンでも、Terraformのwebsite_endpoint
が生成するのは上の方になる。
リージョンはTerraformの変数として用意して、正しいURLを生成するロジックを自前で実装した。
-domain_name = aws_s3_bucket.old_example_files.website_endpoint
+domain_name = "${aws_s3_bucket.old_example_files.bucket}.s3-website.${var.aws_region}.amazonaws.com"
※ ap-northeast-1
以外のリージョンを使う予定はなかったけど、他のリージョンも使使いたい場合は適切なロジックを組めばいい。各リージョンのウェブサイトエンドポイントがこちらで確認できる。
やったことその⑥ HTTPステータスによってリダイレクトするように変更する
ここまで来てやっとリダイレクトできるようになった
ただ、そもそもBucketの中身も消したい…と思った時、この設定ではいけない。S3にファイルがある前提の設定になるので、コンテンツがなくても対応できるように少し変える必要がある。
website {
index_document = "home"
routing_rules = jsonencode([
{
Redirect = {
HostName = "new.example.com"
HttpRedirectCode = "301"
Protocol = "https"
ReplaceKeyWith = "home"
}
+ RoutingRuleCondition = {
+ HttpErrorCodeReturnedEquals = "404"
+ }
}
])
}
RoutingRuleCondition
を追加することで、リクエストされたオブジェクトが見つからない時だけリダイレクトするように変わる。S3 Bucketのすべてのオブジェクトを削除する予定なので、どのリクエストも404になる。つまり、どのリクエストもリダイレクトされる。
最後に
ここまですれば古いサイトへのリクエストはすべて新しいサイトにリダイレクトされるし、古いS3の中身を消してもOK!
1点だけ注意が必要
リダイレクトさせるためにS3のBucket自体とCloudFrontのDistributionが必要になるので、リダイレクトさせたい間はリソースを残し続けないといけない。
リダイレクトをやめて古いサイトを完全に捨てる、ということになったらもちろんこれらのリソースも削除しちゃっても問題ない。