【SPAのSEO】サイトマップを送っても「代替ページ」扱いでインデックスされない問題と戦った話
個人でAI生成BGMの配布サイト(Neura Music Archive)を開発・運営しているのですが、SPA(シングルページアプリケーション)特有のSEOの壁に見事にぶち当たりました。
今回は、Search Console(サチコ)でインデックス数が「1」から全く増えない状態から、Googlebotの誤認を解いて正常にクロールされるようになるまでに実施した対策の備忘録です。
SEO対策を完全に忘れていたプレビュー公開時
事の発端は、サイトのプレビュー版を公開した時のことです。
SPAとしてサクサク動くものはできたものの、SNSで特定のアルバムをシェアしようとした時に致命的な問題に気づきました。
ルーティングをサボっていたため、シェアしても全員がトップページに飛ばされる仕様になっていました。さらに言うと、この段階では index.html に基本的なメタタグ(OGPなど)を仕込むことすら忘れているという有様でした。
URLを振ってサイトマップを送信するも、インデックスされない
これではマズいと思い、各アルバムページに固有のURLを振ることにしました。
手っ取り早く ?theme=ambient&album=album_123 のようなクエリパラメータ付きのURLを動的に生成し、80ページ分のURLを記載した sitemap.xml を作ってサチコに送信しました。
「これで待っていればインデックスされるだろう」と思っていたのですが、数日後にサチコを見ると、インデックス登録済みのページは「1(トップページのみ)」からピクリとも動きません。 URL検査をしてみると、以下のような判定になっていました。
Googleは各アルバムページを見て、「URLのパラメータが違うだけで、中身はトップページと同じでしょ?」と判断し、インデックスを弾いていたわけです。
Googlebotの誤解を解くためにやった3つの対策
このSPA特有の「Googlebotの誤認」を解消するため、以下の3つの修正を行いました。
対策1: index.htmlの静的Canonicalタグの撤去
一番の原因は、index.html の <head> に直接書き込んでいた以下の1行でした。
<link rel="canonical" href="https://neura-music.com/">
Googlebotは、JSが実行されて画面が描画される「前」の静的なHTMLを読み込みます。JSで後から正しいURLに書き換える処理を入れていても、最初の段階で「このページはトップページです」と宣言してしまっていたのです。
index.html からこのタグを削除し、JS側のルーティング処理内で動的にCanonicalタグを生成・挿入するように変更しました。
対策2: クエリパラメータ(?)からパス形式(/)への移行
Googleのクローラーは、URLに ? が含まれていると「既存ページのデータを絞り込んでいるだけ」と判定しがちです。 そのため、URLの設計をパス形式に変更しました。
/?theme=ambient&album=album_123
/theme/ambient/album/album_123
物理的なディレクトリが存在するかのような「クリーンURL」にすることで、独立したページであることをアピールします。
対策3: Cloudflare Pagesでの404フォールバック設定(_redirects)
URLをパス形式に変更すると、SPAの場合はサーバー上に実体ファイルがないため、直接アクセスされた時に 404エラー になってしまいます。
今回はCloudflare Pagesでホスティングしていたため、ルートディレクトリに拡張子なしの _redirects というファイルを作成し、以下の1行を記述しました。
/* /index.html 200
これで、存在しないパスへのアクセスはすべて index.html に転送され、JSが適切にルーティングを処理できるようになります。
まとめ
上記の修正を行い、パス形式に変更した新しい sitemap.xml を再送信して「公開URLをテスト」を実行したところ、無事に「URL は Google に登録できます」のステータスを獲得できました。
SPAは手軽にリッチなUXを作れますが、「URLの設計」「初期ロード時のメタタグ」「サーバーのフォールバック」の3点を初期段階でしっかり設計しておかないと、SEOで痛い目を見ます。同じようにSPAのインデックス登録で詰まっている方の参考になれば幸いです。