アクセス元の国を判定してリダイレクトする仕組みを CloudFront や AWS WAF で構築してみた

2023.12.25

いわさです。

AWS でホスティングしている Web サイトがあって、アクセス元が特定の国の場合はその国向けの Web サイトにリダイレクトさせたいシーンがありました。
グローバル展開している Web サイトなどでよくあるユースケースだと思うのですが、アプリ側判定してリダイレクトさせたことはあったのですが AWS レイヤーで実装したことが無かったやってみました。

CloudFront の場合だと次のように国に応じてアクセス制限することが出来るのですが、今回はリダイレクトをさせたい感じです。

前提として次のように CloudFront で配信される 2 つの Web サイトが存在しているとします。

それぞれの Web サイトは次のように普通にアクセスすることが出来ます。ちょっとレスポンスの内容が違っています。

% curl https://hoge1225public1.tak1wa.com/
hoge1
% curl https://hoge1225public2.tak1wa.com/
hoge2

CloudFront リクエストヘッダーで判定する

方法のひとつとして CloudFront では昔から国コードをリクエストヘッダーに追加する機能があるようです。

CloudFront に設定を追加すると、オリジン宛にCloudfront-Viewer-Countryというヘッダーが追加されます。
こちらに国コードが ISO 3166-1 alpha-2 の形式で設定されるようです。

キャッシュポリシーあるいはオリジンリクエストポリシーに設定を追加します。
今回はキャッシュキーには関心がないのでどちらでも良いのですが、オリジンリクエストポリシーに設定してみました。

このヘッダーはクライアント(ビューアー)が付与するものではなく、クライアントの IP アドレスに基づいて CloudFront がオリジンにリクエストを中継する前にヘッダーを設定するものです。
なので、このヘッダーに基づいて何か挙動を変更したい場合はオリジンのサーバーサイドで処理を行うか、あるいは Lambda@Edge を使ってオリジンリクエストイベントで処理を行う感じになります。リダイレクトくらいだったら CloudFront Functions を使いたい気もしますがやむを得ない。

Lambda@Edge でヘッダーを判定しリダイレクトさせる例は以下に掲載されています。そのまんまです。
今回はこちらを参考にアクセス元が韓国の時だけリダイレクトさせるようにしてみました。

export const handler = async (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;

    if (headers['cloudfront-viewer-country']) {
        const countryCode = headers['cloudfront-viewer-country'][0].value;
        if (countryCode === 'KR') {
            const response = {
                status: '302',
                statusDescription: 'Found',
                headers: {
                    location: [{
                        key: 'Location',
                        value: 'https://hoge1225public2.tak1wa.com/',
                    }],
                },
            };
            return response;
        }
    }
    callback(null, request);
};

オリジンリクエストに設定します。

アクセスしてみましょう。
東京リージョンの CloudShell からだとリダイレクトされていません。

ソウルリージョンの CloudShell からだと、レスポンスが変わりましたね。リダイレクトされました。

AWS WAF のカスタムルールで判定する

先程は CloudFront と Lambda@Edge を使って実施しましたが、単純に条件に一致した場合にリダイレクトするくらいであれば AWS WAF でも良いような気がしてきたのでこちらも試してみました。
カスタムルールで検査対象にOriginates from a country inを使うことで出来て、国が一致するか設定することが出来ます。

ルールに一致した場合にカスタムレスポンスで 302 ステータスと Location ヘッダーを返してやればリダイレクト出来そうです。

設定後、バージニア北部リージョンからアクセスしてみると、リダイレクトされていません。

ソウルリージョンからアクセスしてみると、先程と同じようにうまくリダイレクトされていました。

動的なリダイレクト設定を行おうとすると限界がありそうでなので単純なリダイレクトに限ってしまいますが、AWS WAF の場合だとメンテナンスが簡単かもしれないですね。
あと、CloudFront なしの ALB や API Gateway とかでも使えそうですね。

さいごに

本日はアクセス元の国を判定してリダイレクトする仕組みを AWS サービスで構築してみました。

IP アドレスから国を判定してリダイレクトという方法はどれも同じなのですが、アプリケーションコードに手をいれずに AWS レイヤーで実現しようとするとこの 2 つの方法になりそうでしょうか。
昔 Web システムを構築していた時はこのあたり全てサーバーサイドで処理してしまっていたので、AWS レイヤーで国の判定からカスタムレスポンスの設定まで差し込めるのは便利ですね。