Rso's Jotter

日々の開発の知見のメモやその他雑記

Nuxt.js(SPA), Firebase Hosting, Cloud Function でOGP対応 その2

以前、 タイトルの構成でOGP対応で試行錯誤した内容を書いていたのですが、少しだけ進化したので、その内容をメモしておきます。

rso.hateblo.jp

以前の方法

QuizHubというWebサービスを作っていたのですが、 SPAなので、 /quizzes/:quiz-id のようなクイズ個別ページのURLにアクセスしたときにOGP情報を含んだmetaタグが出力できないので、Cloud Functionを動かしてOGP情報を含むmetaタグを出力し、 /quizzes/show?id=xxx のようなURLにリダイレクトするというやり方をしていました。このやり方では以下のような問題がありました。

  • リダイレクトされたURLをそのまま共有すると、そのURLはOGPがでない。
  • Cloud Functionがそこそこ遅い。

この内の1つ目は、SPAフォールバックの仕組みを理解していなかったので、このへんをうまくやると解消できました。

SPAフォールバックとは

/quizzes/:quiz-id のようなURLはSPAだと実際にこのURLにファイルが存在するわけでないので、vue-routerなどから遷移はできても、リロードしたりURL直指定でアクセスするとページが404になってしまいます。このような場合、 このURL直指定での404を SPAのルートのindex.htmlを返してやることにより、ページを表示させてやることができます。

Nuxtだと以下のように fallback: true を入れてやることにより、SPAフォールバックの設定ができます。

ja.nuxtjs.org

export default {
  generate: {
    fallback: true
  }
}

(catch-allフォールバックとも言うんですかね、正式な定義が分かっておりません..)

Cloud Functionを用いたSPAフォールバックでOGP対応

今回はこのSPAフォールバックを用いて以下のように変更しました。

  1. SPA生成(nuxt generate)時に出力したSPAのルートファイル(index.html)をCloud Functionにもデプロイ
  2. /quizzes/:quiz-id のようなOGP出力したいページにアクセスしたら、 Cloud Functionを起動して、 OGP metaタグを加えて、index.htmlをレスポンスする。

Cloud Functionでの実装のだいたいこんな感じです。

module.exports = functions.region('us-central1').https.onRequest(async function(req, res) {
  // SPAで生成したindex.htmlを読む
  const html = fs.readFileSync("./index.html", 'utf8');

  const dom = new JSDOM(html)
  const document = dom.window.document
  const headElement = document.hea

  /* ...(省略) JSDOMで metaタグを書き換える */

  const updateHTML = document.documentElement.outerHTML

  // 生成したHTMLを返却
  res.set("Cache-Control", "public, max-age=600, s-maxage=600");
  res.status(200).end(updateHTML)
}
   

こうしてやることで、OGP情報を加えた状態でSPA、フォールバックをすることが可能です。 相変わらずこんな方法で正しいのかという気はしますが、一応目指した挙動はしています。

課題

上記の方法により、前回はリダイレクトによるURLが変わってしまう問題は改善できました。(依然Cloud Functionでそこそこ遅いという問題がありますが) また、OGP情報が出力されたとは言え、Google からはほとんどクローリングしてもらえない状態なので、SEO対策は Rendertron などを 使用して別途対策する必要がありそうです(この方法はまた別の機会に書きたいと思います)。