概要
こちらの記事でAWSサービスを使ったドメインリダイレクト手法についてまとめました。
その中でCloudFront関連の方法を試していこうと思い記事にします。
今回は「CloudFront+Lambda@Edge」編です。
実践
構成図
- CloudFrontに代替ドメインでアクセス
- ビューワーリクエストに対してLambda@Edgeで処理(後述)
- 特定のPathの場合にリダイレクト先のALBにリクエストを転送
デプロイ
環境はGitHubで公開しています。
各コードとデプロイ方法を用意してますのでお試しください。
設定値
各ポイントとなる設定値を見ていきます。
CloudFront
ディストリビューションに代替ドメインを設定
オリジンはS3のリージョナルエンドポイント(REST API Endpoint)
S3オリジンにOAC(Origin Access Control)を設定
デフォルトビヘイビアのビューワーリクエストにLambda@Edgeを適用
Lambda@Edge
コード
リクエストのURI(Path)が「/test1/」or「/test2/」の場合に、ALBに設定した独自ドメインにリダイレクトさせるコードです。
def lambda_handler(event, context):
request = event['Records'][0]['cf']['request']
request_path = request['uri']
# Pathチェック
if request_path == '/test1/':
redirect_url = 'https://リダイレクト先FQDN/test1/'
elif request_path == '/test2/':
redirect_url = 'https://リダイレクト先FQDN/test2/'
else:
# マッチしない場合は元のリクエストを返す
return request
# リダイレクトレスポンス
response = {
'status': '301',
'statusDescription': 'MovedPermanently',
'headers': {
'location': [{
'key': 'Location',
'value': redirect_url
}]
}
}
return response
イベントは公式ドキュメントを参考にしています。
IAMロール
信頼ポリシーは以下の通りです。
信頼ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"lambda.amazonaws.com",
"edgelambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
IAMポリシーではCloudWatch Logs関連の許可を付与
- Lambda@Edge用のLambda関数はバージニア北部リージョンで作成する必要がありますが、実行時はリクエスト元の最寄りリージョン別エッジキャッシュにレプリカが作成され実行されます
- そのため、ロググループ作成許可のStatementではリージョンを
*
にしています - Lambda@Edgeのロググループ名は
/aws/lambda/us-east-1.関数名
で作成されるため、
ログイベントは該当のロググループに出力できるように許可しています
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCreateLogGroup",
"Effect": "Allow",
"Action": "logs:CreateLogGroup",
"Resource": "arn:aws:logs:*:AccountId:*"
},
{
"Sid": "AllowPutLogEvents",
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:AccoundId:log-group:/aws/lambda/us-east-1*"
}
]
}
S3
バケットポリシーにCloudFront OACからのアクセス許可を追加
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowCloudFrontServicePrincipal",
"Effect": "Allow",
"Principal": {
"Service": "cloudfront.amazonaws.com"
},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::バケット名/*",
"Condition": {
"StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::アカウントID:distribution/ディストリビューションID"
}
}
}
]
}
動作確認
まず、Lambda@Edgeコードの条件にマッチしないリクエストを呼び出してみます。
ディストリビューションに設定したデフォルトルートオブジェクト(S3バケットにホストしたindex.html)がレスポンスとして返ってきます。
$ curl -i https://ディストリビューション代替ドメイン/
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 316
Connection: keep-alive
x-amz-id-2: EvYdBiurBUqjXHMa0paHa/L+U+AwWgv5vIWT2sFbKV9mzIl+iirLDScFZ8QzMSSmROptIILLu3rP5Kr2mVGLsVuVcJNN8Xjnzw+h46KKf1A=
x-amz-request-id: FYHWE5MFR4EEV3HH
Date: Thu, 20 Mar 2025 15:54:46 GMT
Last-Modified: Thu, 20 Mar 2025 11:43:56 GMT
ETag: "0b51ba8caa5af4d214532a9912de1e23"
x-amz-server-side-encryption: AES256
Accept-Ranges: bytes
Server: AmazonS3
X-Cache: Miss from cloudfront
Via: 1.1 13c972ba4a9ffc82bb0bdabc148e3d20.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: KIX56-P1
X-Amz-Cf-Id: W_JjHiPfCH0riIOifgyVIJTClMWEotmlvo3kZLzGt_MF_rUp2DdyQg==
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>S3</title>
</head>
<body>
<h1>S3 Default Page!</h1>
<p>このページは S3 にホストされています。</p>
</body>
</html>
続いて、Lambda@Edgeコードの条件分岐にマッチするリクエストを送信してみます。
$ curl -i -L https://ディストリビューション代替ドメイン/test1/
HTTP/1.1 301 MovedPermanently
Content-Length: 0
Connection: keep-alive
Server: CloudFront
Date: Thu, 20 Mar 2025 15:57:08 GMT
Location: https://リダイレクト先ドメイン/test1/
X-Cache: LambdaGeneratedResponse from cloudfront
Via: 1.1 a7d4728ae140bd74f2dfa727c541f5e6.cloudfront.net (CloudFront)
X-Amz-Cf-Pop: KIX56-P1
X-Amz-Cf-Id: L2pFCzKmRqjXnoDiavY50-qP2ev3HibG7FP1SHztzKpDlQz-9DGKgg==
HTTP/1.1 200 OK
Date: Thu, 20 Mar 2025 15:57:08 GMT
Content-Type: text/html; charset=UTF-8
Content-Length: 232
Connection: keep-alive
Server: Apache/2.4.62 (Amazon Linux)
Last-Modified: Thu, 20 Mar 2025 11:13:29 GMT
ETag: "e8-630c43a3fe02c"
Accept-Ranges: bytes
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EC2</title>
</head>
<body>
<h1>Web Server Test1 Page!</h1>
</body>
</html>
まとめ
今回は「CloudFront+Lambda@Edge」を試してみました。
「CloudFront Functions」と比較し、より柔軟な処理を実装できる点はメリットだなと感じました。