Help us understand the problem. What is going on with this article?

サーバーレスで動的な OGP を設定する

サーバーレスで動的な OGP を設定する

SNS に自分の作ったページやサービスを配信する際に OGP を設定すると、リッチなコンテンツとして共有することができ、
CTR や CVR などに寄与するかもしれません。

OGP は html の meta タグとして設定しますが、要件によってはページごとに別のコンテンツとして設定したい場合が多々あります。
その場合 meta タグを動的に生成しなくてはならなくなって実装が大変です。

今回は Cloud Functions を用いて比較的簡単に動的な OGP を設定してみます。

構成

スクリーンショット 2020-05-23 12.01.34.png

前提

  • /XXX は共有したいコンテンツで、XXX の部分は可変だと仮定
  • Twitter などに共有する際は /ogp/XXX というリンクを生成してそれをツイートする想定

流れ

  1. /ogp/XXX というリンクにアクセス(ここでいうリンクとはツイートにはられたリンクのこと)
  2. firebase hosting を使って /ogp から始まるパスはすべて Cloud Functions に受け流す
  3. Cloud Functions 側で bot かユーザーを UA から判断
  4. firestore や storage からデータを取得し bot やユーザーに対して適切なコンテンツを作る
  5. それぞれにあったコンテンツを返す

firebase hosting

以下のようにリダイレクトの設定を書きます。

firebase.json
 ...
    "rewrites": [
      {
        "source": "/ogp/**", "function": "ogp"
      },
      {
        "source": "**",
        "destination": "/index.html"
      }
    ]
  ...

Cloud Functions

firebase をバックエンドに用いていたため Cloud Functions を使用しました。
isBot で Twitter bot かそれ以外かを判定します。
あとは firestore からもととなるデータを引っ張ってきて meta タグに設定します。

isBot = false の場合はアクセスされた url から /ogp の部分だけ取り除いた url にリダイレクトして元のコンテンツを返します。

index.ts
export const ogp = functions.region('us-central1').https.onRequest(async (req, res) => {
  const userAgent = req?.headers['user-agent']?.toLowerCase()
  const path = req.path
  const params = path.match(/^\/ogp\/contents\/(.+?)$/)
  const contentId = params?.pop()
  const doc = await db.collection('contents').doc(contentId).get()
  const name = doc.data()?.name
  const description = `${name} の説明`
  const imageUrl = doc.data()?.imageUrl

  const isBot = userAgent !== null && userAgent?.includes('twitterbot')

  if (isBot) {
    // Twitter bot の場合
    // ogp を設定した html を返す
    const html = createHtml(name, description, imageUrl, contentId)
    res.status(200).send(html)
  } else {
    // Twitter bot 以外(ユーザー)の場合
    // ogp を抜いたパスにリダイレクトしてあげる
    const url = `${DOMAIN}/contents/${contentId}`
    res.redirect(url)
  }
})

/**
 * ogp 作成用関数
 */
const createHtml = (
  title: string,
  description: string,
  imageUrl: string,
  contentId: string | undefined) => {
  return `<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>OGP Test</title>
    <meta property="og:title" content="${title}" />
    <meta property="og:image" content="${imageUrl}" />
    <meta property="og:image:width" content="1200" />
    <meta property="og:image:height" content="630" />
    <meta property="og:description"${description}" />
    <meta property="og:url" content="${DOMAIN}/ogp/contents/${contentId}" />
    <meta property="og:type" content="article" />
    <meta property="og:site_name" content="test" />
    <meta name="twitter:site" content="${DOMAIN}" />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="${title}" />
    <meta name="twitter:image" content="${imageUrl}" />
    <meta name="twitter:description" content="${description}" />
  </head>
  <body>For Twitter Bot</body>
</html>
`
}

テスト

開発中にツイートしてしまうとフォロワーさんに迷惑になっちゃうかもなので、 Twitter Card Validator を使ってテストします。
/ogp から始まる URL を貼り付けて設定したタイトルや説明、画像などが表示されたら成功です。

https://cards-dev.twitter.com/validator

参考

https://shuheitagawa.com/set-ogp-tags-with-cloudfunctions/

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした