Firebase Hosting x ReactでOGP対応する
概要
Firebase HostingにデプロイされたReactアプリにて、
Twitterでシェアされた時にページ毎に違うOGPを設定する方法について、備忘録がてら述べます。
経緯
React Helmetを使って動的にOGPを設定しても、TwitterのクローラがJSを実行しないようなので、
ReactプロジェクトでOGPをTwitterで表示する方法として
主に、
- ページ毎にHTMLを事前に生成しておく
- アクセス時に動的にHTMLを生成する
という方法があります。
今回のプロジェクトの都合上、2の方法を選択したのでそのやり方を説明します。
もちろん、お金を使える場合はNetlifyのPrerendering機能を利用したり、
GatsbyJSなどを利用したりしている場合は、1の方法が採用できますね。
Prerendering | Netlify Docs
動的にHTMLを変更する
アクセス時に動的にHTMLを生成する方法としても、
例えばRailsがHTMLを返すシステムの場合、Rails側でリクエストに応じてHTMLを返却すれば問題なさそうです。
ここではFirebase Hostingを使っているケースで、OGPを設定する方法について述べます。
firebase.json の設定
まず、プロジェクトのfirebase.json
で以下のように設定します。
以下の場合/posts/**
にアクセスすると、Firebase FunctionsにデプロイされたaddOgTagsInPost
関数に処理が移譲されます。
その他のページにアクセスすると/index.html
が表示される設定になっています。
ここでsource
やfunction
はよしなに名前を設定してください。
{ "hosting": { ... "rewrites": [ { "source": "/posts/**", "function": "addOgTagsInPost" }, { "source": "**", "destination": "/index.html" } ] }, ... }
関数の実装
上記の例では/posts/**
にアクセスされると、以下の関数が呼ばれるので、
ここでいい感じにHTMLを返してやればいいわけです。
クローラ以外の場合、正規ページにリダイレクトする方法もあるのですが、
OGPが表示されるページと、されないページが存在するのを避けたかったので、
今回は、Firebase Functions側にもHTMLファイルをデプロイしておく方法を採用しました。
以下は例なので、よしなに修正してください。
基本的にはHTMLファイルを読み込んで、</head>
タグの直前にmetaタグを挿入しています。
また、ページに応じて画像のURLを変更しています。
import * as functions from "firebase-functions" import * as fs from "fs" import * as path from "path" export const addOgTagsInPost = functions.https.onRequest((req, res) => { const uid = req.path.split("/")[2] const ogTags = '<meta name="twitter:card" content="summary_large_image"></meta>' + '<meta property="og:title" content="{title}" />' + '<meta property="og:description" content="{description}" />' + `<meta property="og:image" content="https://www.example.com/user_icons/${uid}.png" />` const htmlFile = fs.readFileSync(path.join(__dirname, "./index.html")) const html = htmlFile.toString().replace(`</head>`, `${ogTags}</head>`) res.send(html) })
HTMLファイルのデプロイ
デプロイ前に、デプロイするjsファイルと同じディレクトリ直下に、
フロントエンドで用いているHTMLファイルを設定してください。
package.json
のscripts
に処理を、設定するといいと思います。
問題点
上記のやり方には、以下のような問題点があるので注意してください。
req.path.split("/")[2]
のように/posts/**
の**
の部分の取り方が適当- HTMLが変更された場合に、当該関数の再デプロイが必要
- 忘れがち
- フロントエンドのコードと同じプロジェクトではない場合、どうする?
- Firebase Functionsを挟むので、ロード時間が比較的長くなる
- React-Routerで遷移したなどは、もちろん問題ない
- すでにHTMLにOGPが設定されていた場合、重複するという問題が発生する
</body>
を置き換えるというナイーブな実装
まとめ
以下のようにTwitter上で設定したOGPが表示されました。
※ちなみにこの曲一覧はテスト用なので、本当に僕がよく聴いている曲ではないです。
画像生成については、様々な方法があるので、よしなにやってみてください。
今回は、enPiTという大学の授業でチームで実装したアプリに、OGPを設定しようと思っていろいろやったので、せっかくなので記事化しました。
作成したアプリ↓
https://musicle-app.web.app/