SPAサイトで動的にOGPを変更するための対策を考える
2023-10-06
2024-02-04
今回はSPAアプリにおける動的OGP対策について考えてみたいと思います。このブログもSPAアプリなのですが、現在、X(旧Twitter)でのみ動的なOGPが利用可能となっています。本記事ではそれを実現した手段を含めSPAと動的なOGPについて扱います。
OGPとは
OGPとはOpen Graph Protoclの略で、URLをリンクした際に説明文や画像でそのリンクを飾る仕組みの約束事のことです。XやFacebookにURLを貼ると画像や説明文が表示されると思います。これは貼り付けたURLのheadタグにあるOGPデータを読み込み、それをHTMLに変換して表示しているということです。
このOGPのおかげで、従来は文字だけで表現されていらリンクが画像やページタイトルとともに装飾され、よりリッチで見やすく表示できるようになりました。
なぜSPAで動的なOGP表示が難しいのか
SPAとは
SPAはSingle Page Applicationの略でHTMLページが一つのみで、多くのSPAはClient Side Rendering(CSR)によって描画されます。ここではSPAはCSRであるという前提で話を進めます。CSRではレンダリングは全てクライアントサイド(つまりWEBブラウザ)がJavascriptを実行することで行います。ユーザーからするとページが遷移しているように見えても実際に読み込んでいるHTMLファイルは一つだけで、Javascriptが新たなページをレンダリングしているだけです。メリットとしては、APIを境界としてバックエンドとフロントエンドをきっちり分けて開発を進められるところだと思います。フロントエンドはAPIからのデータ取得と描画に専念すればよく、WEBだけでなくモバイルアプリケーションへの展開も容易です。
クライアントがページを読み込んだ際には空のHTMLファイルとCSS、Javascriptが送信されて、このjavascriptを実行することで初めて描画が行われます。このため、初回アクセス時には全てのjavascriptを読み込んだ後に実行し、非同期通信でAPIと通信してデータを取得するなどの処理が走るため、初回表示は従来のHTMLが送信されてくるWEBページに比べると遅くなる傾向があります。
SPAとOGP
SPA(すなわちCRS)の課題としては初回アクセス時には空のHTMLが送られてくるために、javascriptが実行されるまではクライアントは内容が分かりません。この特性からjavascript非対応のロボットが巡回に来ても空のHTMLを読むだけで、中身がないと思われてしまうことです。
検索クローラに関してはGoogle,Bingのクローラがjavascriptに対応し、SPAであっても検索サイトにインデックスされるようになりました。ただしクローラは非同期通信の終了を待たないので、クローラが正しく解釈しているかは不安が残る、あるいはインデックスに時間がかかるという課題は残っています。その対策としてServer Side Rendering(SSR)があります。
一方、OGPに関してはXのロボットがjavascriptに対応していないため、空のHTMLしか読まれず、OGPデータをロボットに読ませることができないという課題が生じてしまうのです。
OGPだけDynamic Renderringという選択肢
APIと境界としたバックエンドとフロントエンドがきっちりと別れたシンプルな構成や挙動を維持しつつ、OGPデータを取りに来るロボットにはOGPデータを動的に渡したいという良いとこ取りをしたいと考えたとき、どういう手段があるのか考えてみたいと思います。
ここで、通常のユーザーにはSPAとして空のHTML,javascript, CSSを返し、OGPを読みに来たロボットにだけHTMLを返すことができないか?と発想します。このように相手にあわせて描画を変えるのをDynamic Renderingと呼びます。前にも触れましたが、現在ではGoogleやBingの巡回ロボットはjavascript展開ができるのでSEO対策としてのDynamic Renderingは不要と思います。今回はXのOGPロボットをターゲットとしてDynamic Renderingをすることを考えてみます。
尚、GoogleはクローラへのDynamic Renderingを回避策と位置づけており、推奨はしていません(参考)。もしユーザーに表示される内容とクローラに読ませる内容が異なる場合はクローキングを疑われるリスクもあります。本記事ではDynamic Renderingを行うのはOGP用のロボットのみを対象とします。
Rendertoronというツール
2019年のGoogle検索セントラルのブログ記事ではRendertronを使ったDynamic Renderingを説明しています。Rendertronを立ち上げて/render/<URL>
にアクセスするとjavascriptを展開してHTMLを生成してくれます。これをサーバーサイドに設置し、後述するようなnginxのrewrite機能を使うことで特定のUserAgentのみにHTMLを返すことが可能となります。
SSR(Server Side Rendering)との根本的な違いはSSRが初回アクセス時に必ずサーバーサイドでHTML描画処理が走るのに対して、Rendertronを用いてDynamic Renderingを行う場合はロボットがOGPデータ取得に来たときのみ作動し、通常はSPAとして振る舞うことです。このためDynamic Renderingの方がサーバーへの負荷を抑えることができると考えます。
先に述べた通り、Googleの説明ではDynamic Rendering自体が回避策であり、Rendertronの使用は非推奨とされています。あくまでSPAのOGP対策としてのみとして限定的に使用するのならば検討の余地はあるかもしれません。
本ブログでの動的OGP対策例
本ブログはDynamic Renderingはしているのですが、Rendertronは使用していません。RendertoronのGithubのページには非推奨なプロジェクトと記述があり、将来性があまり見込めないと考えています。ではどうやってHTMLを生成しているかというと、実はこのブログのために開発されたバックエンドシステム”Kurolog”にはAPI提供とは別にHTML生成機能があり、別URLでHTMLを生成しています。下記例のようにURLの構成によってSAPアプリを表示する場合とHTMLを表示する場合の構成としています。
URL設定例
SPAの特定のページを表示させるURL
http://example.com/target/
SPAと同一ページのHTML版を表示させるURL
http://example.com/renderhtml/target
ここで通常のユーザーのアクセスの際にはexample.com/target
にアクセスしSPAを表示、Xロボットの場合にはexample.com/renderhtml/target
にアクセスさせHTMLを表示するようにします。これによってロボットには動的なOGPデータを読み込ませることに成功しています。
具体的に上記URLで説明するとnginxでUserAgentで判断して、URLをリライトしてexample.com/target
に来たアクセスをexample.com/renderhtml/target
に変更しています。次の設定例のようにしています。
nginxでの設定例
server {
server_name example.com;
location / {
root /var/www/spa;
index index.html;
try_files $uri $uri/ /index.html;
# X ロボットの場合のみをHTML表示させる
if ($http_user_agent ~* (twitter)) {
rewrite ^/(.*)$ /renderhtml/$1;
}
}
}
これによって通常ユーザーとロボットで読み込ませるデータを変えて、動的なOGP対策を行っています。極端なことを言うとOGPの対策のためだけならば、head
タグにOGP用のmeta
タグを書いて返せばいいだけです。本文をHTMLで描画する必要はありません。もし本文全てをHTMLで描画する手間を省略したい場合はhead
タグのみを返す実装とするのも一つの手だと思います。
Dynamic Renderingの弱点
本ブログで採用した方式ですが、弱点もあります。それはサイト運営者が狙った対象者しか動的なOGPを適用できないことです。本ブログでは動的なOGPデータの適用はX(Twitter)のみで良いと判断したため、この方式を採用しました。しかし運営者が予期せぬアクセスに対してもOGPを動的に適用したいと考えるならばサーバーの負荷と引き換えにSSRを行うという選択肢も出てくると思います。
まとめ
本グログ「KURO DIGITAL LOG」においてSPAのシンプルさとサーバー負荷を考慮しSSRはおこなわずX(Twitter)に対してのみDynamic Renderingを行うことで動的なOGPを実現しました。その際のHTML生成は文字通りサーバーサイドでHaskell製のLucid2を用いて行いました。
将来的にはSNSのOGPロボットがjavascriptを展開してくれるようになるを祈っています。そうすればこのような回避策に手間を取らずともSPAを手軽に開発できるようになると考えます。