やりたいこと
下記の図のように、CloudFrontにワイルドカードのドメインを設定して
- サブドメイン名に応じてS3のフォルダ名にルーティングする
- サブディレクトリのルートオブジェクトに
index.html
を指定する
というのをやってみたいと思います。
CloudFront Functionsについて
以前は、CloudFrontでリクエストによるルーティング処理を行おうとすると、Lambda@Edgeを使う必要がありましたが、 CloudFront FunctionsはLambda@Edgeよりもシンプルな処理に向いているのでこういったリクエストやレスポンスの操作を行うのに向いています。
CloudFront FunctionsとLambda@Edgeの違いや使い分けについてはクラスメソッドさんのブログがよくまとまっているのでこちらを参考にしてください。
やってみる
まずはCloudFront Functionsでファンクションを作成します。
AWSコンソールのCloudFrontのページから、Functionsを選択して新規にFuctionを作成します。
Function code
の Development
に既にサンプルのコードが入力されていますが、それを下記のようなコードに書き換えます。
function handler(event) {
var request = event.request;
var uri = request.uri;
var host = request.headers.host.value;
var prefix = host.split('.', 1)[0]
if (uri.startsWith('/')) {
request.uri = '/' + prefix + uri;
} else {
request.uri = '/' + prefix + '/' + uri;
}
if (uri.endsWith('/')) {
request.uri += 'index.html';
} else if (!uri.includes('.')) {
request.uri += '/index.html';
}
return request;
}
コードを保存した後、Test
タブに切り替えるとコードのテストが行えます。
問題がなければ Publish
タブから Publish function
をクリックします。
Terraform を使って管理する場合は以下のようなコードになります。デフォルトの挙動はpublishまで行うので、既に本番運用しているコードを更新する場合など注意が必要です。
resource "aws_cloudfront_function" "this" {
name = "url-routing"
runtime = "cloudfront-js-1.0"
comment = "Managed by Terraform"
publish = false
code = file("${path.module}/functions/index.js")
lifecycle {
create_before_destroy = true
}
}
次に、CloudFunctionを作成します。
作成したファンクションを Viewer request
に紐付けます
CNAME に *.exmaple.com
のようにワイルドカードのドメインを指定します。
また、HTTPSによるリクエストを受け付ける場合は ワイルドカードに対応した証明書を設定します。
まとめ
同様のことは Lambda@Edge を使ってもできるのですが、 CloudFront Functions を使った方が 1/6 ほど安価で、比較的簡単に導入することができます。
ただ、やはりランタイムがJavaScriptのみ、ライブラリ等も導入できないので複雑な処理は難しいかなという印象です。その場合は素直に Lamba@Edge を使った方がいいと思います。
他にも、CloudFront Functions のサンプルコードは AWS が以下の GitHub レポジトリに公開しているのでこれを参考にするといいと思います。