- SSRとPWAは「相性が悪い」のではなく、キャッシュの担当範囲が違います
- Webアプリは実は多層キャッシュで動いている
- SSRのキャッシュは「HTMLのキャッシュ」
- PWAのキャッシュは「リソースのキャッシュ」
- なぜ「両立しない」と言われるのか
- 両立させる設計
- SSR + PWAで実際に起きる挙動
- 注意すべきポイント
- 結局どちらを選ぶべきか
SSRとPWAは「相性が悪い」のではなく、キャッシュの担当範囲が違います
「SSR(Server Side Rendering)とPWA(Progressive Web App)は両立できないのでは?」という疑問はよく出ます。
結論から言うと、両立は可能です。むしろ、役割を正しく分ければ互いの弱点を補完します。
問題が起きるのは、両者が同じことをしていると誤解している場合です。
SSRは描画の仕組み、PWAはオフライン機能…と説明されがちですが、本質はそこではありません。
SSRとPWAの違いは「描画場所」ではなく、キャッシュをどの階層で行うかです。
この2つは競合する技術ではなく、異なるレイヤーのキャッシュシステムです。
Webアプリは実は多層キャッシュで動いている
Webは単一のキャッシュで動いているわけではありません。少なくとも次の5段階のキャッシュが存在します。
| 層 | 場所 | 主な役割 |
| ブラウザメモリ | JSランタイム | React state / SWRキャッシュ |
| Service Worker | ブラウザ | アプリケーションキャッシュ |
| HTTPキャッシュ | ブラウザ | 静的リソース再利用 |
| CDNキャッシュ | エッジサーバ | 配信高速化 |
| サーバキャッシュ | アプリサーバ | SSRレスポンス再利用 |
SSRとPWAが衝突するのは、この階層を意識せず同じレイヤーを奪い合ったときです。
SSRのキャッシュは「HTMLのキャッシュ」
SSRはサーバでHTMLを生成します。
つまりキャッシュ対象はページではなくレンダリング結果です。
例えばNext.jsでは、ISR(Incremental Static Regeneration)やページキャッシュが行われます。
これは次のようなイメージです。
- ユーザがアクセス
- サーバがHTMLを生成
- CDNに保存
- 次のユーザは生成せず配信
つまりSSRの最適化対象は「初回表示」です。
ここで重要なのは、SSRのキャッシュはブラウザではなくサーバ側に存在するという点です。
PWAのキャッシュは「リソースのキャッシュ」
一方PWAはService Workerを使い、ブラウザ側でキャッシュを行います。
キャッシュ対象はHTMLではありません。
- JavaScript
- CSS
- 画像
- APIレスポンス
つまりPWAは2回目以降の体験を最適化します。
ここで役割がはっきり分かれます。
- SSR:最初の表示を速くする
- PWA:次の操作を速くする
競合していません。
なぜ「両立しない」と言われるのか
原因はService Workerのfetchイベントです。
Service Workerは全HTTPリクエストを横取りできます。
ここで「HTMLまでキャッシュ」してしまうと問題が起きます。
self.addEventListener('fetch', event => { event.respondWith(caches.match(event.request)) })
この設定を行うと、SSRのHTMLが更新されても、ブラウザは古いHTMLを返し続けます。
結果として
- 新しいSSRが反映されない
- デプロイ後も古いUI
- バグ修正が届かない
という事故が起きます。
SSRとPWAが衝突したように見えるのは、キャッシュ対象を誤ったことが原因です。
両立させる設計
SSRとPWAを両立させるには、キャッシュするものを分けます。
HTMLはキャッシュしない(SSRの領域)
JS/CSS/画像はキャッシュする(PWAの領域)
APIは条件付きキャッシュ
具体例です。
self.addEventListener('fetch', event => { const req = event.request if (req.mode === 'navigate') { // HTMLは常にネットワーク event.respondWith(fetch(req)) return } // 静的リソースはキャッシュ優先 event.respondWith( caches.match(req).then(res => res || fetch(req)) ) })
これにより
- 初回はSSR
- 以降はPWA高速化
という構成になります。
SSR + PWAで実際に起きる挙動
正しく構成すると、ユーザ体験は次のようになります。
1回目アクセス
→ SSRにより高速表示(検索エンジンにも有利)
2回目アクセス
→ Service WorkerがJSをキャッシュして即起動
回線が不安定
→ 既存画面は操作可能
このとき重要なのは、PWAはSSRを置き換えません。
SSRの結果を再利用しているだけです。
注意すべきポイント
APIキャッシュの罠
APIレスポンスをPWAでキャッシュすると、次の問題が起きます。
- 在庫情報が古い
- コメントが反映されない
- ユーザ状態がズレる
対策として、次のAPIはキャッシュしない方が安全です。
- 認証情報
- ユーザ固有データ
- リアルタイムデータ
ログインとオフラインの衝突
ログイン後の画面をキャッシュすると、ログアウトしても表示できてしまう場合があります。
これは情報漏洩に近い挙動です。ログイン領域はNetwork Firstが無難です。
結局どちらを選ぶべきか
「SSRかPWAか」という選択はあまり意味がありません。
実際のWebアプリでは、どちらか一方だけで最適化するのは難しいです。
検索流入が重要ならSSRが必要です。
継続利用が重要ならPWAが効きます。
つまり2つは対立概念ではなく、時間軸の最適化です。
- SSRは“最初の5秒”を最適化する技術
- PWAは“使い続ける5分”を最適化する技術
キャッシュの階層を意識して設計すると、両者は矛盾せず共存します。
Webのパフォーマンスはフレームワークではなく、どこにキャッシュを置くかで決まります。
SSRとPWAを分けて考えるのではなく、同じアプリの異なるレイヤーとして設計することが重要です。