この記事は、クラウドワークス Advent Calendar 2024 シリーズ1の24日目の記事です。
「検索順位が急降下してます!!!」
コンサル会社さんからのそんな連絡で始まったできごとです。
できごとの背景
僕が開発に携わってる、フリーランスエージェントサービス、「クラウドワークステック」のユーザーさんが案件を検索する際に使う案件一覧ページに関して、モノリスなRailsアプリケーションからSPAにリプレースしました。
そのリリースがあってから1ヶ月後に外部のコンサル会社さんから
「案件一覧ページの検索順位が急降下してます!!!」
・Googleのクローラーは、一般的に3,000ミリ秒(ms)をひとつの閾値として、
その時点で表示がされているコンテンツをそのぺージのコンテンツとして見るといわれており、
クラウドワークステックの案件一覧ページを見たところ、2,750ミリ秒のタイミングで「404ページ」として表示されています
→つまり、閾値ギリギリのタイミングまで404ページとして表示をされているのではと考えてます
「これはやばい!」
というわけで上記の報告を受けて、当日にエンジニアが集まって技術的な原因と対応策を考えました。
案件一覧ページの検索順位が落ちた原因として、考えられたもの
大きく分けて以下2つの原因がありました。
- ローディング中の404エラー表示
- 初期表示速度の低下
1. ローディング中の404エラー表示
▼事実
- ページローディング中に
404 Not Found
のコンポーネントが挟まる仕様になっていたこと
▼仮説
Google公式の情報ではありませんが、「クローラーは通常、ページの読み込みから約3,000ミリ秒(3秒)後にコンテンツを評価する」と言われており、今回の場合、2,750ミリ秒の時点で404エラーページが表示されていたため、Googleがページを「存在しないページ」と誤って認識してしまい、検索順位が落ちたことに繋がったのでは?
2. 初期表示速度の低下
▼事実
- ページスピードインサイトでパフォーマンスを計測すると、スマホの初回読み込み速度が5秒以上かかっていた
- React側で動的に変更してるメタタグがクローラーに認識されてない
- Reactコンポーネントで動的に変更してる
description
タグ、canonical_url
が空のサイトとして認識されているのがラッコツールで確認できた
- Reactコンポーネントで動的に変更してる
▼仮説
- Reactコンポーネントのレンダリングが完了するまでにクローラーがコンテンツを評価しているため、その間はReactで動的に設定しているメタタグがレンダリングされておらず、結果的に【メタタグが存在しないサイト】として認識されたのでは?
- てか、Reactコンポーネントレンダリング前の空のHTMLファイルをクローラーがコンテンツとして評価しているのでは?
実際にとった解決策
「1. ローディング中の404エラー表示」への対応
- ページローディング中に、「404エラー」テキスト表示のコンポーネントが表示される仕様を撤廃
「404エラー」がクローラーに認識されるよりかは、まだ「ローディング中・・・」だけがクローラーに認識されるほうがマシだよねという結論になりました。
「2. 初期表示速度の低下」への対応
空のHTMLの状態をクローラーが読み取っているのは、SEO的にかなり悪影響と思い、こちらの対応を急ぎました。
対応としては、
- APIレスポンスに2時間のキャッシュを設定
全員のユーザーが案件一覧ページにリクエストした時のスマホでの初期表示速度が5秒以上かかってたのはさすがにイケてなかったので、Fastlyを活用してAPIレスポンスのキャッシュを設定しました。
実際にページスピードインサイトで計測すると
First Contentful Paintという「ユーザーがアクセスしてから画面にコンテンツの一部が表示されるまでのスピード指標」がスマホでは0.9秒になっています。
この対応によって、初期表示速度が大幅に向上し、特にスマートフォンでの表示速度が大幅に改善されました
落ち着いてみて、考察
前提として、
1年前からモノリスRails→SPAへのリプレース作業が始まったプロジェクトで、当時僕は違うチームにいて技術的な意思決定には関わっていなかったので、SPAが採用された詳細な背景は分かりませんし、当時のメンバーの判断をリスペクトしてます。
そのうえで振り返ってみると、
SEOで重要なページである案件一覧ページをリプレースする時の技術選定時に初回読み込みが遅くなるCSR(クライアントサイドレンダリング)のSPAをチョイスするのではなく、初回読み込みが高速のSSR(サーバーサイドレンダリング)を採用し、ReactフレームワークのNext.jsなどを用いて、SSRを実現すれば良かったのではと思いました。
▼CSRとSSRの違いについてわかりやすい動画です
参照