概要
SPAで特定のページをTwitterやFacebookでシェアさせたい場合があると思います。
しかし、OGPの仕組み上、TwitterやFacebookのクローラーに対してmeta tagを表示させてあげる必要があり、クライアントサイドでの書き換えを行っても、これに反映させることはできません。
これを解決する方法としては、
- ダイナミックレンダリングする
参考: ダイナミックレンダリングの使用方法
クローラーかどうかを判断し、クローラーの場合には用意してある静的なコンテンツを読み込ませる方法です。ブログなど、コンテンツの修正の頻度が小さいものであれば、私はこの方法を選択しています。
これによって、クローラー側ではJavascriptを実行する必要がなくなります。
- SSR (サーバーサイドレンダリング)する
NextJsなどがフレームワークとして代表的ですが、サーバーサイドでコンテンツを形成してから、フロントエンドに返します。
- Meta tagのみ、SSRする
今回の記事の対象です。
全てSSRほどの手間がかからず、OGP対策のみを考えているのであれば、これを選択するべきだと思っています。
ただし、アプリのユースケースによって、そこまで編集がされないコンテンツなのであれば、ダイナミックレンダリングを考えてもいいのかなと思います。
今回、バックエンド側はCloud Functionsでの実装が前提です。
実装の概要
ざっくりとやろうとしていることを図にしました。
Cloud FunctionでSSRする
SSRの対象
正確に言うと、SSR時でメタタグの書き換えを行う対象ユーザーです。
今回は、以下のクローラー(Bot)であれば、書き換えを行うこととし、一般ユーザーからのアクセスに対しては書き換えを行いません。
その理由は、タグの書き換えのためにFirestoreなどのDBにアクセスし取得する負荷を減らすためです。
UserAgentから、botかどうかの判定をするコードは以下です。
Cloud Functionsにリクエストを送信する
次に、特定のURLにアクセスされた時に、Cloud Functionsにリクエストを送信するために、以下のようにfirebase.jsonを設定します。
リライトルールを使用すると、特定のパターンに一致するリクエストを単一の宛先に送ることができます。たとえば、Hosting サイトの
参照: Cloud Functions による動的コンテンツの配信/bigben
ページからすべてのリクエストを送信してbigben
関数を実行するには、firebase.json
を開き、hosting
セクションの下に次のrewrite
構成を追加します。
Build時に、index.htmlをコピーしておく
フォルダ構成によりますが、Build後のタイミングで、Cloud Functionsから読み込める階層にindex.htmlをコピーするようにタスクを記述して上げることで、自動化します。
package.json
"build": "react-scripts build && cp build/index.html functions/hosting/"
Cloud Functionの実装
以下が完成したコードです。
処理の流れとしては、
① index.htmlを読み込み、Stringに変換する
② Bot(クローラー)でなければ、処理せずにindex.htmlを返す。Botであれば、DBから取得したデータを使い、正規表現で置換して、新規にindex.htmlを作成する
となっています。
注意点として、Cloud Function
Fetch as Googleしてみる
ここまででSPAでmeta tagを書き換え、クローラーに読み込ませることができました。
それでは、実際のページについてはどうでしょうか。
以下がFetch as Googleした結果で、Botからみたアプリなのですが、クライアントサイドで、読み込み中に表示させているgifが表示されています。
ダイナミックレンダリングや、全体のSSR化で解決できる問題ではあるのですが、フロント側で処理速度を上げてみた後に考えることなのかなと思って、今対応しています。
(追記)
Firebaseの各種サービスのロケーションを東京に変更して、パフォーマンスの向上をはかってみました。これで、サードパーティを含めホスティングされているリージョンが東京になりました。
結果として、Botからコンテンツの閲覧が可能になりました!
やはり、パフォーマンスをあげるというアプローチがまず取り組むことなのかなと実感した次第です。
Before
After