Edited at

cloud frontとlambda@edgeでUA判定をしてPCページとSPページを出し分けてみた


cloud frontとlambda@edgeでUA判定をしてPCページとSPページを出し分けてみた

Webサービスを構築する仕事がきたのですが、ビジネスロジックが不要なサービスだったので

できるだけカンタンに作ろうと思いサーバーレス構築に挑戦してみた際にやってみたレポートになります。


使ったAWSのサービス


  • cloud front

  • lambda( @Edgeはバージニア北部リージョンで関数作成する必要があります

  • S3

  • Route53(ドメイン設定の話なので今回は特に登場させません)

  • ACM(SSL証明書の話なので今回は特に登場させません)

  • Cloud Watch(lambdaを利用したら自動でついてきた)


やったこと


  1. S3にHTMLを配置

  2. cloud frontとS3を紐付け

  3. ドメインにアクセスした際にどのHTMLを出すか設定(トップページの設定)

  4. lambda@edgeにUA判定処理を実装

  5. lambda@edgeとcloud frontの紐付け(トリガー設定)今回はViewerリクエストをトリガーに設定

ざっくり動くまででだいたい2〜3時間くらいでできました。


lambda側に設定する必要のあるポリシーの信頼関係※記載わすれてたので追記しました(2019/07/10)

アクセス権限はS3とCloudFrontによしなに設定してあげてください。

お手軽にやるならフル権限つけてしまってよいかと思います。

{

"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": {
"Service": "edgelambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

1〜3はいろいろなところに記事が落ちているのでlambdaのソースと引っかかったポイントを紹介します。


lambdaに実装したソース

'use strict';

const whitelist = [
'android',
'iphone',
'ipad'
];

const isSpBrowser = (uas) => {
if (uas && Array.isArray(uas) && uas.length > 0) {
return uas.some(ua => whitelist.some(w => ua.value.toLowerCase().indexOf(w) !== -1));
}
return false;
};

// User-AgentからPC・SP判定を行ない描画ページを切り替える
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const headers = request.headers;

// console.log('headers.user-agent. = ' + headers['user-agent']);
if (headers['user-agent']) {
// URI整理
// cloud frontにトップページとして設定したindex.htmlがURIに入ってくるので除去
var uri = request.uri.slice(-10) === ('index.html') ? request.uri.slice(0, -10) : request.uri;
uri = uri.slice(-1) === ('\/') ? uri : uri + '\/';
console.log('headers[user-agent] = ' + headers['user-agent'][0].value);
// URI判定
// ファイル読み込みで呼び出された場合中断
if(uri.match(/( \.png|\.css|\.js|\.jpeg|\.jpg|\.bmp|\.gif|\.pdf|\.svg|\.zip)/)) {
callback(null, request);
return;
}
// UA判定
if (isSpBrowser(headers['user-agent'])) {
uri = uri + 'SP用描画ファイル名'
} else {
uri = uri + 'PC用描画ファイル名'
}
// XXX 現状パラメータを渡すことができない
// uri = param === '' ? uri : uri + '?' + param
console.log('uri = ' + uri);
request.uri = uri
callback(null, request);
return;
}
callback(null, request);
};

PC/SPの判定においてはこういう書き方もできます。(というか今はこっちのほうが推奨されてます)

※下記ソースで行う場合トリガーをオリジンリクエストイベントに設定する必要があります。

(ビューワーリクエストイベントの後に CloudFront-Is-*-Viewer ヘッダーを追加されるため)

    // UA判定 前述コードのUA判定部分と置き換える

if (headers['cloudfront-is-desktop-viewer']
&& headers['cloudfront-is-desktop-viewer'][0].value === 'true') {
uri = uri + 'PC用描画ファイル名'
} else if (headers['cloudfront-is-mobile-viewer']
&& headers['cloudfront-is-mobile-viewer'][0].value === 'true') {
uri = uri + 'SP用描画ファイル名'
} else if (headers['cloudfront-is-tablet-viewer']
&& headers['cloudfront-is-tablet-viewer'][0].value === 'true') {
uri = uri + 'SP用描画ファイル名' // タブレットでSP画面で見せる場合
} else if (headers['cloudfront-is-smarttv-viewer']
&& headers['cloudfront-is-smarttv-viewer'][0].value === 'true') {
uri = uri + 'PC用描画ファイル名' // SmartTVVでPC画面で見せる
}

参考:

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/header-caching.html#header-caching-web-device

https://docs.aws.amazon.com/ja_jp/AmazonCloudFront/latest/DeveloperGuide/lambda-examples.html#lambda-examples-redirecting-examples


ポイント


  1. ログはCloud Watchに出力されるがアクセスされたcloud frontのリージョンに出力されたのでlambdaを配置しているリージョンではない

  2. ドキュメントは基本英語版が正

  3. トリガー設定はlambda側からやるほうがらく

  4. 困ったら迷わず有料サポートを使う(結果そのほうが早いのでトータルでの費用が下がる)


最後に

イベントページなどの後ろにビジネスロジックがいらないサービスであればこの構成は結構おすすめできると思います。

インフラ作業部分(エンジニアが必要な作業分)は大体5人日程度で収まったのでその分デザインやプロモーションにコストがまわせるのも強みかと

※初回なのでこれくらいですが慣れればもっと短くなる予感