CloudFront の Origin を、ボットかどうか(User-Agentによって)判定して切り替える

Webpack でビルドしたWebアプリを S3 にデプロイし、S3 + CloudFront でホストしていました。

ブラウザからのリクエストは問題ないのですが、検索エンジンやTwitterなどのボットからのアクセスの場合、JSを動かせないため内容のほぼ無いページが評価されてしまい、情報の価値がありません。

そのため、Lambda@Edge + CloudFront で、User-Agent を判定し、JSを動かせないようなUAの場合はオリジンをサーバサイドレンダリングするサーバに切り替えるようにしました。

Lambda@Edge とは

Lambda ファンクションを CloudFront のエッジサーバにデプロイし、動作させることができます。

CloudFront で動作させる箇所を4箇所から選ぶことができ、オリジンサーバへリクエストする際に Lambda ファンクションでリクエストヘッダを変更したり、オリジンサーバを変更したりできます。

AWSのページにわかりやすい図があります

ちなみに、Lambda@Edge について以前書いたブログ記事もあります

失敗例

AWSの図を見る限り、Origin Request で Lambda ファンクションを動かし、
リクエストヘッダの User-Agent ヘッダで Origin を変えれば良さそうです。

実際にそのようなコードは書けるのですが、Origin Request の際は User-Agent ヘッダは常に
「Amazon CloudFront」になるため、判定には使えません。

なお、CloudFront のビヘイビアの設定で、ユーザー端末の User-Agent ヘッダをオリジンまで通すことは可能です。
(ビヘイビア→ Cache Based on Selected Request Headers → Whitelist Headers で、User-sAgent を Add Custom)

ただし、これを行うと警告が出ます。

Some headers have a lot of possible values, and caching based on the values in these headers would cause CloudFront to forward more requests to your origin. We recommend that you do not cache based on the following headers:User-Agent

この警告にある通り、キャッシュの効率が悪くなるので、よほどの理由がない限りやらないほうが良いでしょう。

方針

このような方針にしました。

1. Viewer Request に入ってきた時点で、Lambda@Edge で User-Agent ヘッダを判定。
ホワイトリストで評価し、もしJSを扱えないようなUA(ボット等)の場合、
bot-user-request=1 のリクエストヘッダを付与する。

2. CloudFront のビヘイビアで、bot-user-request のHTTPヘッダは通す (Cache Based on Selected Request Headers にする)

3. Origin Request の時点で、Lambda@Edge で bot-user-request ヘッダがあれば、オリジンのドメインを変更

コード

Viewer Request

'use strict';

/*
Cloudfront の Viewer Request 用のファンクション。

Google Bot からのアクセスの場合、Httpヘッダの bot-user-agent = "1" を設定する。
この後、CloudFront のビヘイビアで bot-user-agent のヘッダーを通し、
さらに Origin Request のLambda で、bot-user-agent を判断し、
Originを切り替える
*/

const bots = [
'Twitterbot',
'facebookexternalhit',
'compatible; Google',
'Googlebot',
];


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

const isBot = bots.some(v => {
return request.headers['user-agent'][0].value.includes(v)
});
if (isBot) {
request.headers['bot-user-agent'] = [
{
'key': 'bot-user-agent',
'value': '1'
}
];
}

callback(null, request);
};

Viewer Response

'use strict';

/*
bot-user-agent の HTTPリクエストヘッダがあったら、
オリジンを書き換える。

*/
const newDomainName = 'new.example.com';

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

if ('bot-user-agent' in request.headers) {
if ('custom' in request.origin ) {
request.origin.custom.domainName = newDomainName;
request.headers['host'] = [{ key: 'host', value: newDomainName}];
// HTTPSでリクエストする場合
request.origin.custom.port = 443;
request.origin.custom.protocol = 'https';
}
}

callback(null, request);
};
Current rating: 5

コメント

コメントを投稿
コメントするには TORICO-ID にログインしてください。
ログイン コメント利用規約
Search

Recent Tweets

  • ytyng

    ytyng @ytyng

    #ダンディナイト 2日間お疲れ様でした!照明、PA、映像配信オペレーターしました。漫画家の先生方やすごいアーティストの方々とイベントできてとても楽しかったです!
    1 week, 2 days ago

  • 濱田潤

    濱田潤 @hamada_TORICO

    ytyng

    #ダンディナイト ・ワン 閉幕。 2日間たくさんの皆様にご来場いただき、ライブ配信でもご参加いただき本当にありがとうございました! 多忙な中イベントにご参加いただいた参加メンバーの皆様にも改めて心から感謝!感謝!! ワンがあ… https://t.co/8RnqWWaDQW
    1 week, 2 days ago

  • 安藤拓郎 TAKURO ANDO

    安藤拓郎 TAKURO ANDO @takuroando

    ytyng

    #ダンディナイト ライブありトークありドローイングありカレーありコーヒーあり、こういうのやりたかったってイベントやらせていただいた。関係者の皆様、来場いただいた皆様、ありがとうございました! #池袋虜 https://t.co/qIAs2Gfmnr
    1 week, 2 days ago

  • マンガ展/TORICO

    マンガ展/TORICO @manga10_torico

    ytyng

    🎊【DANDY NIGHT.1/#ダンディナイト ・ワン】produced by #ダンディさん🎊 DAY2ライブデーオープン🎊 初日ご来場の方もそうでない方もぜひ #池袋虜 にお越し下さいませ〜😘 ライブドローイングやミュー… https://t.co/uzk8zeS2kB
    1 week, 2 days ago

  • 濱田潤

    濱田潤 @hamada_TORICO

    ytyng

    そして!#ダンディナイト 本日ライブ配信!凄くない!? 御三方が描くとこ見れるんですよ! 15時半からtwitterとyoutubeでライブ配信開始! マンガ展(@manga10_torico )アカウントをフォローしてご覧… https://t.co/kop6T20jcw
    1 week, 2 days ago