11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Amplify Consoleでホスティングした静的サイトの前段にCloudFrontを建ててLambda@EdgeでカスタムOGP対応する

Last updated at Posted at 2021-05-08

Amplify Consoleでホスティングした静的サイトの前段にCloudFrontを建ててLambda@EdgeでカスタムOGP対応する

Amplify Consoleでホスティングしている静的サイトがあります。
このサイトはOGP対応をしていますが、どのページも同じ内容で表示されてしまいます。

SPA(Single Page Application)ではなくSSG(Static Site Generation)でビルドすることにより、個別ページのOGP設定ができるそうなのですが、ビルド時に決まらない、IDでGETしたページに対してカスタムOGP画像を表示したいのです。

(1) https://yoteiasobi.w2or3w.com/
(2) https://yoteiasobi.w2or3w.com/calendars/ic019t7gn99ckp27vtjjm2d6ro@group.calendar.google.com
どちらも以下のように同じOGP画像が表示されてしまうのですが、
image.png

これを、(2)の場合は以下のように独自のOGP画像が表示されるようにしたい。
(独自のOGP画像は予め作成しておくこととする。)
image.png

それを実現するための記事となります。

現在の設定を確認する

まず初めに、Amplify ConsoleRoute53の設定を確認しておきます。

備考 : 独自ドメインのサブドメインをAmplify Consoleで割り当てる方法については以前、以下の記事に書きました。
お名前.comで取得した独自ドメインのサブドメインだけでなくルートドメインも割り当てる
お名前.comで取得した独自ドメインのサブドメインをAmplify Consoleで割り当てる

Amplify Console

Amplify Console > ドメイン管理
mainブランチhttps://yoteiasobi.w2or3w.com が割り当てられています。
image.png
アクション > HTTPS証明書の表示
アクション > DNSレコードの表示
この辺りの設定も確認しておきます。
image.png
この辺の設定は、Amplify Consoleが自動的に設定してくれていたものです。後ほどドメイン割り当てを削除した上で、同じような設定をCloudFrontに対して行うことになります。

Route53

Route53 > ホストゾーン > yoteiasobi.w2or3w.com
image.png
AレコードCNAMEレコードはAmplify Consoleのドメイン割り当てにより自動的に設定されたものです。
こちらもAmplify Consoleのドメイン割り当てを削除した後に、設定しなおすことになります。

Amplify Console で独自ドメイン割り当てを削除する

それでは取り掛かってゆきます。
まずは、Amplify Consoleで設定している独自ドメインの割り当てを削除します。

Amplify Console > ドメイン管理
アクション > 削除
image.png
削除されました。
image.png
この時点で、 https://yoteiasobi.w2or3w.com/ からはサイトへアクセスできなくなります。

Route53のホストゾーンも確認してみましょう。
Route53 > ホストゾーン > yoteiasobi.w2or3w.com
image.png
Amplify Consoleで割り当てドメインを削除すると、Aレコードも削除されることが見て取れます。
CNAMEは残ってますが、後ほど削除して新たに登録することになります。

CloudFrontでディストリビューションを作成する

続いて、CloudFrontでディストリビューションを作成します。
CloudFrontにアクセスし、CloudFront Distributions > Create Distribution ボタンを押下。
image.png
Select a delivery method for your content. で、Get Started ボタンを押下。
image.png
Create Distributionで、以下を設定。

  • Origin Domain Name : Amplify Consoleの目的のブランチのドメインを設定
  • Minimum Origin SSL Protocol : 必要に応じて設定を変更
  • Allowed HTTP Methods : 必要に応じて設定を変更
  • Alternate Domain Names (CNAMEs) : 今は空でいいです (後で設定します)
  • SSL Certificate : 今は Default CloudFront Certificate (*.cloudfront.net) のままでいいです (後で設定します)

Create Distributionボタンを押下。
image.png
image.png
image.png
image.png
ディストリビューションが作成されました。
image.png
CloudFrontが発行したアドレスでサイトが表示されること、また、証明書の内容を確認しましょう。
image.png

CloudFrontのディストリビューションに独自ドメインと証明書を割り当てる

独自ドメインでサイトにアクセスできるようにします。また証明書も発行して割り当てます。

CloudFront Distributionsで目的のディストリビューションを選択、GeneralタブのEditボタンを押下。
image.png
Edit Distribution画面で以下を設定します。
Alternate Domain Names(CNAMEs) : 目的のドメイン
証明書が無いので Custom SSL Certificate (example.com) ラジオボタンが無効状態です。
新たに証明書をリクエストするため、Request or Import a Certificate with ACM ボタンを押下します。
image.png
証明書のリクエスト画面で、以下のように設定を進めてゆきます。

  • ドメイン名の追加 : 目的のドメインを追加する。
  • 検証方法の選択 : DNSの検証 を選択する。
  • タグを追加 : 必要に応じてタグを設定する。
  • 確認 : 内容を確認して「確認とリクエストボタン」を押下する。
  • 検証 : 内容を確認して「続行」ボタンを押下する。(CNAMEの値は後で使います)

image.png
image.png
image.png
image.png
image.png
証明書が発行されました。
image.png

Route53の目的のホストゾーンへレコードを作成します。
Route53 > ホストゾーン > yoteiasobi.w2or3w.com
レコードを作成ボタンを押下。
image.png
レコードを作成画面にて、AレコードとCNAMEレコードを作成します。
Aレコードの作成

  • レコード名 : 空
  • レコードタイプ : A
  • エイリアス : ON
  • トラフィックのルーティング先 : CloudFront ディストリビューションのエイリアス
  • ディストリビューションを選択 : CloudFront の目的のディストリビューションのドメイン名

image.png

CNAMEレコードの作成

  • レコード名 : 目的のドメインに発行した証明書のCNAMEレコード名
  • レコードタイプ : CNAME
  • 値 : 目的のドメインに発行した証明書のCNAMEレコード値

image.png
レコード名や値はCertificate Managerで目的のドメインを選択することで確認できます。
https://console.aws.amazon.com/acm/home
image.png

もともとあったCNAMEレコードは削除してください。

それでは、CloudFront Distributionsの、目的のディストリビューション編集画面へ戻りましょう。
目的のディストリビューションに作成した証明書を割り当てます。
Custom SSL Certificate (example.com) ラジオボタンを選択し、先ほど発行したドメインの証明書を選択します。
image.png
編集を完了したら、ドメインでアクセスできること、そして証明書の内容を確認してみましょう。
image.png

LambdaをCloudFrontのEdgeに割り当てる

いよいよ Lambda@Edge の設定です。

私てっきりLambda@Edgeというサービスがあると思っていたのですが、そうではないんですね。
『LambdaをCloudFrontのディストリビューションへエッジ機能として割り当てる』ことをそう呼んでいるみたいです。

なお、CloudFrontから呼び出すLambdaは、リージョンを「バージニア北部」にする必要があります。
また、CloudFrontから呼び出すLambdaがサポートしているランタイムは以下のみです。(2021年5月現在)
(※最新の情報は公式のページを参照ください。)

Python 3.8, 3.7
Node.js 12, 10

という事で、バージニア北部にLambdaを作成しましょう。私は Python 3.8 で作成しました。名前はyoteiasobi-ogpです。
リージョン以外にも、以下を選択した上で作成します。

  • デフォルトの実行ロールの変更 : AWSポリシーテンプレートから新しいロールを作成を選択。
  • ポリシーテンプレート : 基本的な Lambda@Edge のアクセス権限 (CloudFront トリガーの場合)を選択。

image.png

Lambdaのコードを書いたらデプロイし、さらに、新しいバージョンを発行する必要があります。
CloudFrontのディストリビューションへLambdaのARNを設定するのですが、特定のバージョンを参照する必要があります。
(そうしないとエラーとなって設定できません。)
image.png
image.png
image.png

Lambdaをデプロイしてバージョンを発行したら、ARNをコピーして、それをCloudFrontのディストリビューションへエッジ機能として割り当てます。
image.png
CloudFront Distributions の目的のディストリビューションを選択。
BehaviorsタブでDefault(*)を選択しEditボタンを押下。
image.png
Edit Behavior画面のEdge Function Associationsで以下の設定をします。

  • Edge Function : Lambda@Edgeを選択
  • CloudFront Event : Viewer Requestを選択
  • Function ARN/Name : LambdaのARN(バージョン発行したもの)を設定

さてこれで、サイトにアクセスしたらLambdaが呼ばれるようになりました。Lambda@Edgeの完成です!

Lambda@Edgeのログ参照時の注意点

Lambdaが実行されたことを確認するためにCloudWatch Logsを参照することになるわけですが、知っておくべきことがあります。
CloudWatchのロググループは/aws/lambda/function-nameではなく/aws/lambda/us-east-1.function-nameへ出力されます。
LambdaのモニタリングからCloudWatchのログを表示すると/aws/lambda/function-nameへ飛ぶので注意が必要です。
image.png
また、ログが出力されるリージョンはクライアントから最も近いロケーションとなります。
日本からWebサイトへアクセスした場合は「東京」、twitterbotからの場合は「北カルフォルニア」でした。

Lambda@Edgeで例外が発生した場合

Lambdaで例外が発生した場合、503エラーが返ってしまいます。
image.png

503 ERROR
The request could not be satisfied.
The Lambda function associated with the CloudFront distribution is invalid or doesn't have the required permissions. We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.

カスタムOGP対応するLambda(Python)

CloudFrontのViewer Requestで呼ばれるLambdaでヘッダの内容を書き換えてOGPの画像を変更します。
サンプルコードを載せておきます。

lambda_function.py
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
BOTS = [
    "twitterbot",
    "facebookexternalhit"
]
def lambda_handler(event, context):
    logger.info("=== START ===")
    logger.info(json.dumps(event, ensure_ascii=False, indent=2))
    
    request = event["Records"][0]["cf"]["request"]
    headers = request["headers"]
    uri = request["uri"];
    
    if "user-agent" not in headers:
        return request
    
    userAgent = headers["user-agent"][0]["value"].lower()
    logger.info("request uri = {0}".format(uri))
    logger.info("userAgent = {0}".format(userAgent))
    
    list = [item for item in BOTS if item in userAgent]
    logger.info(list)
    
    isBot = len(list) > 0
    logger.info("isBot = {0}".format(isBot))

    if not isBot:
        return request
    else:
		customOgpImageAddress = "https://w2or3w.s3-ap-northeast-1.amazonaws.com/yoteiasobi/images/ic019t7gn99ckp27vtjjm2d6ro%40group.calendar.google.com.png"
		customOgpDescription = "YOTEIASOBIのサンプルカレンダー by w2or3w"
        return {
            "status": 200,
            "statusDescription": "OK",
            "headers": {
                "content-type": [{
                    "key": "Content-Type",
                    "value": "text/html"
                }]
            },
            "body": getContent(customOgpImageAddress, customOgpDescription)
        }

def getContent(customOgpImageAddress, customOgpDescription):
    return (''
        '<!doctype html>'
        '<html>'
          '<head>'
            '<title>YOTEIASOBI</title>'
        	'<meta data-n-head="1" charset="utf-8">'
        	'<meta data-n-head="1" data-hid="description" name="description" content="よてい で あそぼ!YOTEIASOBI は Googleカレンダーを ゆるっと共有 できる サーバーレスWebアプリです。">'
        	'<meta data-n-head="1" data-hid="og:site_name" property="og:site_name" content="YOTEIASOBI">'
        	'<meta data-n-head="1" data-hid="og:type" property="og:type" content="website">'

        	'<meta name="twitter:card" content="summary_large_image">'

        	'<meta property="og:url" property="og:url" content="https://yoteiasobi.w2or3w.com/calendars/ic019t7gn99ckp27vtjjm2d6ro@group.calendar.google.com">'
        	'<meta property="og:title" property="og:title" content="YOTEIASOBI">'
        	'<meta property="og:image" property="og:image" content="{0}">'.format(customOgpImageAddress) + 
        	'<meta property="og:description" property="og:description" content="{0}">'.format(customOgpDescription) + 
          '</head>'
          '<body>'
          '</body>'
          '</html>'
        )

OGP画像生成をテストするツール

上記コードは、TwitterやFacebookからシェアしようとした際にカスタムOGP画像に差し替える実装をしています。
動作確認する際に毎回実際に投稿するのだと大変ですが、その代わりに以下のサイトを利用するとはかどります。
https://cards-dev.twitter.com/validator
https://developers.facebook.com/tools/debug/

あとがき

5/1の「浜松ITもくもく会」でコレをやろうと思ってたのですが、諸事情で出来ませんでした。

それから1週間かけて、なんとかQiitaに記事書くところまでできたから良かったです。
(厳密にいうと、アプリがOGP画像を生成するところはまだ未着手なのですが、まぁこれはそのうちやります!)

11
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?