はじめに

CloudFrontを利用しているWebサイトでドメイン変更を行った際、旧ドメインから新ドメインへリダイレクトが必要となるケースがありました。
このとき、単にトップページへリダイレクトするだけでなく、旧ドメインのURL構造(パスやクエリパラメータ)を新ドメインに引き継いだ状態でのリダイレクトも求められました。
AWSの公式情報では、こうしたリダイレクトを実現する方法として、以下の3つの手法が紹介されています。

  • ① S3ウェブサイトエンドポイント
  • ② Lambda@Edge
  • ③ CloudFront Functions

参照元:CloudFront でドメインをリダイレクトする方法を教えてください。

最終的には、要件を整理したうえで各手法の特性と実装コストを比較した結果、CloudFront Functionsを用いた実装方法を選定しました。
本記事では、その選定に至るまでの検討内容と、最終的に採用した CloudFront Functionsによるリダイレクト設定の手順をご紹介します。

①S3ウェブサイトエンドポイント

AmazonS3の「静的ウェブサイトホスティング」機能を活用し、旧ドメインから新ドメインへリダイレクトをを行う方法です。

設定手順

1. S3バケットを作成し、「静的ウェブサイトホスティング」を有効化
2. ホスティングタイプで「オブジェクトのリクエストをリダイレクトする」を選択
3. リダイレクト先のドメインを設定
4. CloudFrontのオリジンにS3のウェブサイトエンドポイントを指定


S3のリダイレクト機能は、すべてのリクエストを特定の固定ドメインへ転送する形式であったため、パスやクエリパラメータを引き継ぐといった今回の要件を満たすことができませんでした。
そのため、今回のケースでは要件に適さないと判断し、S3単体でのリダイレクト方法は採用を見送りました。
もしシンプルなトップページへのリダイレクトであれば、設定も簡単で有効な選択肢だと思います。

②Lambda@Edge

Lambda@Edgeは、Lambda関数をCloudFrontのエッジロケーションで実行できる仕組みです。
リクエストに含まれるパス、クエリパラメータ、ヘッダー情報などをもとに処理できるため、柔軟な動的リダイレクトが可能になります。

設定手順

1. Lambda@Edge実行用のIAMロールを作成
必要なポリシーはこちらを参照:Lambda@Edge 用の IAM アクセス許可とロールのセットアップ
2. Lambda関数を作成
CloudFrontに紐づけるため、バージニア北部リージョンで作成する必要があります。
ランタイムはpythonかNode.jsとなります。以下はpythonの設定例です。

def lambda_handler(event, context):
    # CloudFront からのリクエスト情報を取得
    request = event['Records'][0]['cf']['request']
    uri = request.get('uri', '')
    querystring = request.get('querystring', '')

    # リダイレクト先のURLを構成
    redirect_url = 'https://newdomain.com' + uri

    # クエリパラメータが存在する場合、それも含める
    if querystring:
        redirect_url += '?' + querystring

    # リダイレクトレスポンスを返す(301 Moved Permanently)
    return {
        'status': '301',
        'statusDescription': 'Moved Permanently',
        'headers': {
            'location': [{
                'key': 'Location',
                'value': redirect_url
            }]
        }
    }

3. Lambda@Edgeをデプロイ

4. CloudFrontのビヘイビアにLambda@Edgeを適用
ビューワーリクエストイベントに関連付けて有効化します。

Lambda@Edgeは今回の要件を満たしますが、このサービスは次のような高度な処理が求められる場面で活躍しそうです。

  • 完了までに数ミリ秒以上かかる関数。
  • 調整可能な CPU またはメモリを必要とする機能。
  • サードパーティライブラリに依存する関数 (他の AWS のサービスとの統合のため、AWS SDK を含む)。
  • 外部サービスを使用して処理するために、ネットワークアクセスを必要とする関数。
  • ファイルシステムへのアクセスまたは HTTP リクエストの本文へのアクセスを必要とする関数。

参照元:CloudFront Functions と Lambda@Edge の違い

今回の要件自体は、「パスやクエリを新ドメインに引き継いでリダイレクトする」という比較的シンプルなものであったため、このような高度な機能を持つLambda@Edgeを使うのはややオーバースペックであると判断しました。このあと紹介する CloudFront Functionsでも、同様のリダイレクトがより軽量かつシンプルに実装可能と見込まれたため、他の選択肢も含めて検討を続けることにしました。

③CloudFront Functions

CloudFront Functionsを使って、リダイレクト処理をCloudFrontのエッジロケーションで直接実行する方法です。
軽量かつ高速に動作し、構成もシンプルに保てるのが特長です。

設定手順

1. CloudFront Functionsを作成
CloudFront FunctionsはJavaScriptコードのみ対応となります。

function objectToQueryString(obj) {
    var str = [];
    for (var param in obj)
        if (obj[param].multiValue)
            str.push(param + "=" + obj[param].multiValue.map((item) => item.value).join(','));
        else if (obj[param].value == '')
            str.push(param);
        else
            str.push(param + "=" + obj[param].value);

    return str.join("&");
}

async function handler(event) {
    const request = event.request;
    const newurl = https://newdomain.com;

    // クエリパラメータが存在する場合、それも含める
    const queryString = Object.keys(request.querystring).length ? ?${objectToQueryString(request.querystring)} : '';

    // redirect
    const response = {
        statusCode: 301,
        statusDescription: 'Moved Permanently',
        headers: {
            "location": { "value": newurl + request.uri + queryString }
        }
    };

    return response;
}

2. CloudFrontのビヘイビアにCloudFront Functionsを適用
ビューワーリクエストイベントに関連付けて有効化します。

CloudFront Functionsは、軽量で実行時間の短い関数に適したソリューションとされています。
公式ドキュメントでは、以下のようなユースケースへの適用が紹介されています。


参照元:CloudFront Functions と Lambda@Edge の違い

今回の要件は 「URLリダイレクトまたは書き換え」に該当し、CloudFront Functionsで問題なく実現できました。
シンプルなJavaScriptによる条件分岐だけで要件を満たせるため、構築・運用のコストを抑えつつ、より簡潔に実装できたのが選定の決め手です。
Lambda@Edgeと比べて、デプロイの手軽さや料金面でも優位性があったため、今回は CloudFront Functionsを採用しました。

最後に

今回の要件では、URLパスやクエリパラメータを引き継ぐリダイレクトが必要だったため、軽量かつ低コストな CloudFront Functionsを選定しました。
ヘッダー情報や外部APIを利用した高度な制御が必要な場合は、Lambda@Edgeも選択肢となりそうですし、要件次第ではS3も選択肢になり得ます。
いずれの方法を選ぶにしても、まずは要件を明確に整理した上で、過不足のない構成を選ぶことが運用コストや保守性の面でも重要だと感じました。