レンダリングパターン — CSR / SSR / SSG / ISR
CSR、SSR、SSG、ISR。頭字語が並ぶと別世界の技術に見えますが、違っているのは HTML をいつ、どこで作るか の一点だけです。この軸で並べ直すと、それぞれが何を得て何を失っているかが見えます。
この章で学ぶこと
- 4 つのパターンが HTML をいつ・どこで生成するか
- 初回表示・データの鮮度・SEO・サーバー負荷のトレードオフ
- 要件からパターンを選ぶ手順
- 「どれか 1 つを選ぶ」ではなくページ単位で混在させられること
宣言的 UI と命令的 UI で扱った、JavaScript が DOM を組み立てるという前提を使います。
この章で扱わないこと
Next.js や React での実装方法は既存のガイドが扱います。本章はパターンの選び方に絞ります。
- React ガイド — Next.js 入門 — CSR / SSR / SSG の書き方
一つの軸で並べる
左に行くほど事前に作り込んであるので速く、右に行くほどリクエスト時点の情報を使えるので新しい。速さと鮮度のどちらを優先するかが選択の本質です。
CSR — ブラウザで組み立てる
サーバーは中身の入っていない HTML と JavaScript を返し、ブラウザが JavaScript を実行して DOM を組み立てます。
<!doctype html>
<html lang="ja">
<head>
<title>My App</title>
<script src="bundle.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
ここで押さえるべき点があります。**CSR でもサーバーから HTML は返ってきます。**返ってくるのが中身の無い骨組みだというだけで、「HTML を一切取得せず JavaScript だけを取得する」わけではありません。ブラウザは HTML を要求し、その中の script タグを見て JavaScript を取りに行きます。HTML が無ければ何も始まりません。
| 内容 | |
|---|---|
| 初回表示 | 遅い。HTML → JS ダウンロード → 実行 → データ取得 → 描画の順に待つ |
| データの鮮度 | 常に最新。表示の直前に取得する |
| SEO | クローラーが JavaScript を実行しないと中身が見えない |
| ページ遷移 | 速い。差分データだけ取得して DOM を書き換える |
| サーバー負荷 | 低い。静的ファイルを配るだけ |
管理画面やログイン後のダッシュボードのように、SEO が要らず操作が続く画面に向いています。
SSR — リクエストのたびにサーバーで作る
リクエストを受けるたびにサーバーがデータを取得し、完成した HTML を返します。
| 内容 | |
|---|---|
| 初回表示 | 中身の入った HTML が届くので、見えるまでは速い |
| データの鮮度 | 常に最新 |
| SEO | 有利。HTML に中身が入っている |
| ページ遷移 | 素の SSR では毎回サーバーに行く |
| サーバー負荷 | 高い。リクエストごとにデータ取得とレンダリングが走る |
誤解が生じやすいのはサーバー負荷とレスポンス速度です。**SSR はレスポンスが最も速いパターンではありませんし、サーバー負荷も最小ではありません。**リクエストのたびにデータベースへ問い合わせて HTML を組み立てるので、あらかじめ作ってある SSG や ISR より最初の 1 バイトが返るまでの時間 (TTFB) は長くなり、サーバーの仕事量も多くなります。SSR が買っているのは速さではなく 鮮度と SEO です。
なお SSR で返した HTML はそのままでは静的な文字列です。ボタンを押しても反応しません。JavaScript を読み込んでイベントを結び付ける処理 (ハイドレーション) が終わって初めて操作できるようになります。「見えているのに押せない」時間が生まれるのは、この段差が理由です。この待ち時間は Core Web Vitals の応答性の指標に現れます。押しても反応しないのは、その間ハイドレーションがメインスレッドを占めているためです (イベントループ)。
SSG — ビルド時にすべて作っておく
デプロイのビルド時にすべてのページを HTML として生成し、CDN から配ります。
| 内容 | |
|---|---|
| 初回表示 | 最も速い。CDN のエッジから完成品を返す |
| データの鮮度 | ビルド時点で固定される |
| SEO | 有利 |
| サーバー負荷 | 最小。オリジンサーバーへの到達すら不要 |
| 弱点 | 内容の更新に再ビルドが要る。ページ数が多いとビルドが長い |
ここにも誤解があります。**SSG で生成したページは「完全に静的で、クライアント側のデータ取得と組み合わせられない」わけではありません。**生成されるのは普通の HTML と JavaScript なので、読み込まれたあとにブラウザが API を叩いて画面の一部を差し替えられます。
実際によく使われる構成です。
- 記事本文は SSG で静的化して SEO と表示速度を取る
- いいね数やコメント数だけクライアントから取得して最新にする
- ログイン状態に応じたヘッダーもクライアント側で描き替える
「静的な骨格 + 動的な一部」は標準的な作り方で、MDN もサーバーが骨組みを返してクライアントが追加データを取得する組み合わせを勧めています。
ISR — 静的に配りつつ、期限が来たら作り直す
SSG の弱点は「更新に再ビルドが要る」ことでした。ISR はここを埋めます。ビルド時に静的生成しておき、指定した時間が過ぎたあとのアクセスをきっかけに、そのページだけを裏で作り直します。
Next.js での書き方は次のとおりです。
// 60 秒経過後の最初のリクエストをきっかけに、このページを再生成する
export const revalidate = 60;
export default async function Page() {
const posts = await fetch('https://example.com/api/posts').then((r) => r.json());
return <PostList posts={posts} />;
}
動作の順序が重要です。
- ビルド時にページを生成してキャッシュする
- 期限内のリクエストにはキャッシュをそのまま返す
- 期限が切れたあとの最初のリクエストにも、まず古いキャッシュを返す (待たせない)
- 同時に、裏側で新しいページの生成を始める
- 生成が成功したら差し替える。次のリクエストからは新しいページが返る
つまり「期限切れの瞬間に誰かが待たされる」ことはありません。古い内容を一度だけ踏むかわりに、常に速く返せます。再生成が失敗した場合は、最後に成功した内容が配られ続けます。
期限で待つ以外に、更新をきっかけに明示的に作り直す方法もあります。
| 方式 | 使い方 | 向いている場面 |
|---|---|---|
| 時間ベース | revalidate = 3600 のように秒数を指定 | 更新のタイミングが読めないもの |
| オンデマンド | 更新処理から revalidatePath() / revalidateTag() を呼ぶ | 管理画面での更新を即座に反映したいもの |
Next.js のドキュメントは、時間ベースの値は短くしすぎず 1 時間程度から始め、精度が要るならオンデマンドを使い、本当にリアルタイムが要るなら動的レンダリングへ切り替えることを勧めています。
比較表
| CSR | SSR | SSG | ISR | |
|---|---|---|---|---|
| HTML を作る場所 | ブラウザ | サーバー | ビルド時 | ビルド時 + 裏で再生成 |
| 初回表示 | 遅い | 中 | 速い | 速い |
| データの鮮度 | 最新 | 最新 | ビルド時点 | 指定した期限まで古い |
| SEO | 不利 | 有利 | 有利 | 有利 |
| サーバー負荷 | 低い | 高い | 最小 | 低い |
| 更新の反映 | 即時 | 即時 | 再ビルド | 期限後 or オンデマンド |
どう選ぶか
判断は 3 つの問いで絞れます。
- 検索エンジンに拾わせる必要があるか。 不要なら CSR で十分です。管理画面やログイン後の画面が該当します。
- 内容はリクエストごとに変わるか。 ユーザーごとに違う、あるいは秒単位で変わるなら SSR です。
- 変わらない、または多少古くてよいか。 更新頻度が低いなら SSG、たまに更新されるなら ISR です。
そして重要な前提があります。**これはアプリ全体で 1 つ選ぶものではありません。**Next.js のようなフレームワークではページ単位で指定できます。実際のサイトはたいてい混在します。
- トップページと記事ページ → SSG または ISR
- 検索結果ページ → SSR
- マイページ → CSR
よくある誤解
「CSR は初回アクセス時にサーバーから HTML を一切取得しない」 — 取得します。中身が空の骨組みの HTML が返り、その中の script タグから JavaScript を読み込みます。
「サーバーで HTML を作るほうが、静的ファイルを配るより速い」 — 逆です。事前に作ってある SSG が最も速く、サーバーの負荷も最小です。SSR はリクエストのたびに HTML を組み立てる処理を払って、鮮度と SEO を得ています。
「SSG はクライアント側のデータ取得と組み合わせられない」 — 組み合わせられます。生成されるのは通常の HTML と JavaScript なので、読み込み後に API を呼んで一部を更新できます。
「ISR は期限が切れた瞬間のアクセスが待たされる」 — 待たされません。まず古いキャッシュを返し、再生成は裏で走ります。
「レンダリングパターンはアプリで 1 つ選ぶ」 — ページ単位で選べます。混在が普通です。
確認問題
問 1. 1 日 1 回更新される社員数百人向けの社内お知らせ一覧があります。検索エンジンには載せません。どのパターンが適切ですか。
答え: CSR、または SSG とクライアント取得の組み合わせ。
判断の 3 つの問いに当てはめます。
- SEO は不要 (社内向け) → CSR が候補に入る
- リクエストごとに変わるか → 変わらない (1 日 1 回)
- 多少古くてよいか → よい
SEO が不要で規模も小さいので、CSR でシンプルに作るのが妥当です。ログイン後の画面ならなおさら SSR や SSG の恩恵が薄くなります。
一覧の骨格だけ SSG で配り、中身をクライアントから取得する構成も成立します。SSR を選ぶ理由はこの要件には見当たりません。
問 2. 記事が数万件あるメディアサイトで、記事は公開後もときどき修正されます。SEO は最重要です。SSG と ISR のどちらを選びますか。
答え: ISR
SSG だと 2 つの問題が出ます。
- 数万ページを毎回ビルドするので、
next buildの時間が現実的でなくなる - 記事を 1 件直すだけでサイト全体の再ビルドとデプロイが要る
ISR ならビルド時に生成しつつ、期限後のアクセスで該当ページだけ作り直せます。修正を即座に反映したいなら、記事の更新処理から revalidatePath() を呼ぶオンデマンド方式を併用します。
SEO については SSG も ISR も HTML に中身が入るので差はありません。差が出るのはビルド時間と更新の反映方法です。
問 3. SSR で作ったページが「表示はされているのにボタンを押しても反応しない」時間があります。なぜですか。
答え: ハイドレーションがまだ終わっていないためです。
SSR がサーバーから返すのは完成した HTML ですが、その時点ではイベントハンドラが結び付いていない静的な文字列です。ブラウザが JavaScript をダウンロードして実行し、既存の DOM にイベントを結び付ける処理 (ハイドレーション) が完了して初めて操作できるようになります。
見た目が先に届き、操作可能になるのが後という段差がここで生まれます。この待ち時間は操作への応答性の指標に影響します。JavaScript のバンドルを小さくする、必要な部分から順に有効化する、といった対策が取られます。
まとめ
- 4 つのパターンの違いは HTML をいつ・どこで作るか の一点です
- CSR でも HTML は返ってきます。中身が空の骨組みだというだけです
- SSR は鮮度と SEO を買うパターンで、レスポンス速度もサーバー負荷も最良ではありません
- SSG は最も速くサーバー負荷も最小ですが、更新に再ビルドが要ります。クライアント側のデータ取得とは併用できます
- ISR は静的配信を保ちながら、期限切れ後に古い内容を返しつつ裏で再生成します。オンデマンド再生成も使えます
- パターンはページ単位で選べます。混在させるのが普通です
- React ガイド — Next.js 入門 — CSR / SSR / SSG の実装方法
- MDN — クライアントサイドレンダリング
- Next.js — Incremental Static Regeneration
次に読む
- 楽観的更新 — 描画したあと、操作への反応をどう速く見せるか
- HTTP キャッシュと CDN — ISR と同じ「古いものを返しつつ裏で更新する」考え方が HTTP 層にもあります