以前、 タイトルの構成でOGP対応で試行錯誤した内容を書いていたのですが、少しだけ進化したので、その内容をメモしておきます。
以前の方法
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フォールバックの設定ができます。
export default { generate: { fallback: true } }
(catch-allフォールバックとも言うんですかね、正式な定義が分かっておりません..)
Cloud Functionを用いたSPAフォールバックでOGP対応
今回はこのSPAフォールバックを用いて以下のように変更しました。
- SPA生成(nuxt generate)時に出力したSPAのルートファイル(index.html)をCloud Functionにもデプロイ
/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 などを 使用して別途対策する必要がありそうです(この方法はまた別の機会に書きたいと思います)。