PWA。速いし、体験も良い。 でも、SEOを考えると、キャッシュの設計が、思ったよりずっと難しい。ここが、たぶん一番の肝。
ユーザー体験と、Googlebotへの「おもてなし」。この二つは、時々、逆の方向を向く。 この矛盾をどう解決するかが、今日のテーマ。
なぜ問題になるのか?
話は単純で。
Service Workerがコンテンツをキャッシュしてくれると、ユーザーは次から表示がすごく速くなる。これは良いこと。 UXが向上するし、表示速度はSEOの重要な要素でもあるから。
でも、Googlebotがクロールに来た時、もしService Workerがキャッシュした古いコンテンツを返してしまうと、新しい情報がインデックスされない。 これが最悪のシナリオ。
ユーザーには速度を、クローラーには鮮度を。この両立が課題になる。
キャッシュ戦略の選択肢
Service Workerでのキャッシュ方法、いくつか代表的なものがある。 どれを選ぶかで、SEOへの影響が大きく変わってくる。
- Network First: まずネットワークに確認しに行く。取得できなかったらキャッシュを見る。一番安全だけど、PWAの速さというメリットが少し薄れる。
- Cache First: まずキャッシュを見に行く。あればそれを表示する。一番速い。でも、更新されたコンテンツが反映されないリスクが一番高い。 静的なアセット(CSSとかフォントとか)には良いけど、記事みたいなコンテンツには危険。
- Stale-While-Revalidate: まずキャッシュを表示しつつ、裏でネットワークに新しいコンテンツがないか確認しにいく。 もし新しいのがあれば、次回の表示からそれに差し替える。ユーザーの体感速度と情報の鮮度を両立しやすい、かなり有力な選択肢。
じゃあ、どう設計する?
現実的な落としどころは、たぶん「分離」と「使い分け」。
ひとつは、PWAの「App Shell(アプリの骨格部分)」と「コンテンツ(中身)」でキャッシュ戦略を分けること。
- App Shell: ヘッダーやフッター、ナビゲーションといった滅多に変わらない部分は、`Cache First`で積極的にキャッシュする。
- コンテンツ: 記事や商品情報など、頻繁に更新される部分は、`Stale-While-Revalidate`や`Network First`を使う。 これで、クローラーが常に新しい情報を見つけやすくなる。
もう一つの強力な選択肢が、動的レンダリング(Dynamic Rendering)。 これは、リクエストしてきたのがユーザーかクローラーかを判別して、提供するものを変える手法。
- ユーザーには: 普通にJavaScriptで動くPWAを返す。
- クローラーには: サーバー側で事前にレンダリングした静的なHTMLを返す。
Googleは、両者で見せるコンテンツが同じであれば、これをクローキング(不正行為)とは見なさないと明言している。 これなら、クローラーの確実なインデックスと、ユーザーの快適な体験を両立できる。
Workboxでの実装例" width="800" height="480" loading="lazy" decoding="async" style="max-width:95%;height:auto;cursor:pointer" class="no_autoresize">
各戦略のざっくり比較
自分用のメモとして、表にまとめてみる。
| 戦略 | ユーザー体感速度 | コンテンツ鮮度(SEO) | 個人的な感想 |
|---|---|---|---|
| Cache First | 最速。オフラインでも強い。 | 非常に危険。更新が全く反映されない可能性。 | コンテンツには絶対使っちゃダメなやつ。App Shell専用かな。 |
| Network First | 遅い。毎回ネットワーク待ち。 | 安全。常に最新。 | PWAにする意味が薄れるかも。でも確実性をとるならこれ。 |
| Stale-While-Revalidate | 速い。キャッシュをすぐ表示するから。 | ほぼ安全。初回アクセスは古いが、裏で更新がかかる。 | バランス型。ほとんどのケースでこれが最適解になりそう。 |
| 動的レンダリング | 速い(ユーザーはPWA)。 | 最も安全(クローラーには静的HTML)。 | 実装コストはかかるけど、一番理想的な形かもしれない。 |
注意点:結局はURL
どんなに高度なことをしても、忘れてはいけない基本がある。それは、PWA内の各ページが、独立した一意のURLを持つこと。
SPA(Single Page Application)のフレームワークを使っていると、画面遷移はしてもURLが変わらない実装をしがちだけど、GooglebotはURLを単位として世界を認識している。 クロール可能なURLがなければ、インデックスもされない。
これは本当に基本的なことだけど、一番見落としやすい罠かもしれない。
結論、というか考え事
完璧な「正解」はない。サイトの特性による。
毎日何度も更新されるニュースサイトと、月に一度しか更新されないブログでは、最適なキャッシュ戦略は当然違う。
基本は `Stale-While-Revalidate` を軸に考えつつ、絶対に落とせないコンバージョンに関わるページや、SEOで重要なページだけ `動的レンダリング` を導入する…みたいなハイブリッドな形が、現実的なのかもしれない。
結局のところ、技術に振り回されるんじゃなくて、ユーザーと検索エンジンの両方に、何を提供したいのかを考えることから始めるべきなんだろうな、と。
あなたのサイトでは、どのキャッシュ戦略を使っていますか?あるいは、どれが一番合っていると思いますか?
