Lambda@Edgeを使ってリファラによってリダイレクトさせる

運営中のサイトに対して、特定の流入経路によるアクセスを、別サイトに誘導したいことがあります。

例えば、イベントにスポンサーとして協賛し、イベントサイトのスポンサーロゴは会社ホームページのURLになっているとします。 このスポンサーロゴを経由したアクセスを、イベントに合わせて構築したポータルサイトに誘導するにはどうすればよいでしょうか?

Lambda@Edge を使うと、既存のアプリケーションに手を加えることなく、リクエスト時のリファラによってリダイレクトできます。

前提

Lambda@Edge を利用するため、Amazon CloudFront を利用していることが大前提となります。

要件

  • HTTPリクエスト時のRefererヘッダーの URI が event.com/summits/ を含む場合、https://portal.com/ に 302 でリダイレクトさせる。
  • 条件を満たさない場合、リクエスト内容は変更しない。

実装方針

ビューワーリクエスト・イベントで Lambda@Edge を呼び出します。

このイベントは CloudFront がビューワーからリクエストを受け取った後、リクエストされたオブジェクトがエッジキャッシュにあるかどうかを確認する前に関数が実行されます。

今回のケースでは、Refererヘッダーを確認し、リダイレクト対象の場合は、リダイレクト処理を促すレスポンスをクライアントに返します。

リダイレクト対象でない場合は、そのままCloudFront・オジンサーバーで処理します。

Node.js のコード

Lambda@Edge のランタイムには Node.js 8.10 を利用します。

また、CloudFront と連携するため North Virginia リージョンで作成します。

'use strict';

exports.handler = (event, context, callback) => {
    const request = event.Records[0].cf.request;
    const headers = request.headers;
    const event_url = 'event.com/summits/';
    
    if (headers['referer'] &&
        headers['referer'][0].value.indexOf(event_url) !== -1) {
        const response = {
            status: '302',
            statusDescription: 'Found',
            headers: {
                location: [{
                    key: 'Location',
                    value: 'https://portal.com/',
                }],
          },
        }
        // リダイレクト用レスポンスを返す
        callback(null, response);
    };
    // CloudFront/Originでリクエストを処理
    callback(null, request);
};

リファラヘッダーをチェックしている箇所を強調しました。

リファラがリダイレクト対象の場合、302 のレスポンスを作成します。

  • リファラの部分一致に利用する URI
  • リダイレクト先 URI

を環境変数で管理できると楽なのですが、Lambda@Edge の制約から、環境変数は使えません。

CloudFront との連携

作成した Lambda 関数をCloudFront と連携します。

CloudFront event には Origin request を選択します。

Lambda関数はリクエストヘッダーしか確認しないため、リクエストのボディー部分は不要です。 そのため、"Include body" のチェックは不要です。

CloudFront の管理画面から Lambda@Edge との紐づけを行う場合、Behavior から行います。

Lambda Function の ARN 指定では、バージョンも含めた ARN である必要がある点に、ご注意ください。

動作確認

Lambda@Edge のデプロイ完了後、実際にリファラを変えながらリダイレクト動作を確認します。

リダイレクト対象のリファラがある場合

リファラがリダイレクト対象の場合、302 ステータスでリダイレクト先(Location) を返します。

$ curl -D - \
  http://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2019/01/httpstls.oguri_.classmethod.infoindex.html-2019-01-25-06-37-20.png \
  -H 'Referer: http://event.com/summits/tokyo/foo'
HTTP/1.1 302 Found
Content-Length: 0
Connection: keep-alive
Server: CloudFront
Date: Fri, 15 Feb 2019 23:20:30 GMT
Location: https://portal.com/
X-Cache: LambdaGeneratedResponse from cloudfront
Via: 1.1 27b16a0c069e2a271545e30400f5a415.cloudfront.net (CloudFront)
X-Amz-Cf-Id: AbPwN1ISe0aKDhnTCEQftnrh9jpquAPKImMzqkIj5Hh5aWddpqvICQ==

X-Cache の値(LambdaGeneratedResponse from cloudfront)から、レスポンスは Lambda@Edgeが返していることがわかります。

リファラがない場合

リファラがない場合、リクエストはCloudFront/Origin サーバーで処理されます。

$ curl -D - \
  http://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2019/01/httpstls.oguri_.classmethod.infoindex.html-2019-01-25-06-37-20.png
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 13
Connection: keep-alive
x-amz-id-2: wvz7AQVA2GwM/UCQaK4ee6ta2Tta+9iQFZtSE98bSoyu4H/AhrFYK5AOT3PPVFm9lCozFUPPZKw=
x-amz-request-id: 6ABE7E614292CCAF
Date: Fri, 15 Feb 2019 23:19:38 GMT
Last-Modified: Fri, 15 Feb 2019 15:27:33 GMT
ETag: "c897d1410af8f2c74fba11b1db511e9e"
Accept-Ranges: bytes
Server: AmazonS3
X-Cache: Miss from cloudfront
Via: 1.1 342c9b6ea7230889a22dcc532c1d3136.cloudfront.net (CloudFront)
X-Amz-Cf-Id: fYs8D32KSsBKY-zw2e3f6ph2FecVrS1Uh7Z-6hl6XCgMLVtBxrVXcQ==

hello world!

リダイレクト対象外のリファラがある場合

リファラがリダイレクト対象外の場合、リクエストはCloudFront/Origin サーバーで処理されます。

$ curl -D - \
  http://cdn-ssl-devio-img.classmethod.jp/wp-content/uploads/2019/01/httpstls.oguri_.classmethod.infoindex.html-2019-01-25-06-37-20.png \
  -H 'Referer: http://google.com/foo/bar/baz'
HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 13
Connection: keep-alive
x-amz-id-2: wvz7AQVA2GwM/UCQaK4ee6ta2Tta+9iQFZtSE98bSoyu4H/AhrFYK5AOT3PPVFm9lCozFUPPZKw=
x-amz-request-id: 6ABE7E614292CCAF
Date: Fri, 15 Feb 2019 23:19:38 GMT
Last-Modified: Fri, 15 Feb 2019 15:27:33 GMT
ETag: "c897d1410af8f2c74fba11b1db511e9e"
Accept-Ranges: bytes
Server: AmazonS3
Age: 102
X-Cache: Hit from cloudfront
Via: 1.1 b9f07fa5534a4d783d0891d44cc959c9.cloudfront.net (CloudFront)
X-Amz-Cf-Id: wW71TSDNFL8zBC-77XiQ8T6NlpGB5tCS68dhFxqJlKtvQw3tSR_eiQ==

hello world!

最後に

Lambda@Edge を使い、既存アプリケーションに手を加えることなく、リダイレクトする方法を紹介しました。

今回はロジック判定にリファラヘッダーを利用しましたが、ユーザーエージェントなど、他のフィールドを利用することも可能です。

リクエスト・レスポンスにちょっとした処理を入れたい時に、Lambda@Edge は使い勝手が良いですね。

それでは。