Twitterカードに代表されるOGPの実装は、基本的にmetaタグに情報を加えるだけ。
ただ、React等SPAとして実装されたサイトでOGPを実装する場合は一工夫必要。
OGPの基本的な仕組み
- ユーザーがSNS(Twitterなど)にリンクを貼る
- 各SNSのボットがそのURLにアクセスして、metaタグを読みにいく
この時、ボットがJSを実行できないために、
SPAの場合は常にルートのindex.htmlのmetaタグが読み込まれてしまう
=表示される画像などを動的に変更することができない
指針
- CloudFrontのビューワーリクエストにLambda@Edgeを差し込む
- UserAgentを判定し、botからのアクセスの時だけ、動的にmetaタグを追加したHTMLをレスポンスとして返す
ユーザーがSNSにリンクを貼ってボットがサイトに来たときだけ、表示させたい画像をmetaタグに詰めて渡してあげる
実装
Lambda@Edgeの実装
Lambda@Edgeはus-east1(バージニア北部)リージョンでしか使用できないため、
それ以外のリージョンにいる場合は切り替えてから関数を作成する。
コードは https://qiita.com/kurimoto/items/6212372ead6522161a60 を参考にした。
ユーザーからのリクエストを読み取って、UserAgentを判定し、Twitterbot等の場合だけ作成したレスポンスを返す。
'use strict';
const bots = ['Twitterbot'];
exports.handler = (event, context, callback) => {
const request = event.Records[0].cf.request;
const userAgent = request.headers['user-agent'][0].value;
const isBot = bots.some(v => {
return userAgent.includes(v);
});
if (isBot) {
const body = getContent()
const response = {
status: '200',
statusDescription: 'OK',
headers: {
'content-type': [{
key: 'Content-Type',
value: 'text/html'
}]
},
body: body
};
callback(null, response);
} else {
callback(null, request);
}
};
const getContent = () => {
return = `
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<meta content="text/html; charset=UTF-8" http-equiv="Content-Type" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<meta content="R-bies,INC." name="author" />
<title>タイトル</title>
<meta name="twitter:card" content="summary" />
<meta property="og:url" content="http://example.com/" />
<meta property="og:title" content="Example" />
<meta property="og:image" content="${imageUrl}" />
</head>
<body>
</body>
</html>
`;
};
上のコードはそのまま動かない。(metaタグに設定しているimageUrlが定義されていないから)
UserAgentを判定する
あらかじめ指定したボットだった場合は動的にタグを追加してレスポンスとして返す。
それ以外はリクエストをそのまま実行する(S3にアクセスする)
というものになっている。
ので、動的に画像を差し替えたりしたいときは、条件を追加したり、URLに応じてDBからfetchしてきたりして、imageUrlを定義してあげればよい。
参考
CloudFront経由のイベントは下記のフォーマットで渡ってくるので、
テストをする際は下記をコピペしてあげたり、イイ感じに改変すればよい
{
"Records": [
{
"cf": {
"config": {
"distributionDomainName": "d111111abcdef8.cloudfront.net",
"distributionId": "EDFDVBD6EXAMPLE",
"eventType": "viewer-request",
"requestId": "4TyzHTaYWb1GX1qTfsHhEqV6HUDd_BzoBZnwfnvQc_1oF26ClkoUSEQ=="
},
"request": {
"clientIp": "203.0.113.178",
"headers": {
"host": [
{
"key": "Host",
"value": "d111111abcdef8.cloudfront.net"
}
],
"user-agent": [
{
"key": "User-Agent",
"value": "Twitterbot"
}
],
"accept": [
{
"key": "accept",
"value": "*/*"
}
]
},
"method": "GET",
"querystring": "",
"uri": "/example/5674591867371520"
}
}
}
]
}
Lambda@Edgeへのデプロイ
関数を作成してテストも通ったら、Lambda@Edgeへデプロイする。
関数設定のトリガーを追加するところから、CloudFrontを選択
デプロイするディストリビューションを設定
イベントをビューアーリクエストに設定
追加する
Lambdaの実行ロールの設定
下記を貼る
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": [
"edgelambda.amazonaws.com",
"lambda.amazonaws.com"
]
},
"Action": "sts:AssumeRole"
}
]
}
CloudFrontの設定
コレをしないと延々503が出て困る
デプロイしたディストリビューションのbehaviorを追加する
Lambdaを実行するパスのパターンを設定する。全てで動かす場合は *
実行させるLambdを設定する
ARNはLambda画面の右上にある
これで追加すると、CloudFrontにデプロイされ、
晴れてOGPタグを動的に差し替えることに成功する