Astro 5.1 On-Demand Rendering で動的コンテンツを効率化する実装ガイド【2026年5月最新】
Astro 5.1のOn-Demand Rendering機能を使い、リクエストごとに動的生成するページと静的ページを混在させる実装方法を解説。SSGとSSRの良いとこ取りでパフォーマンスとSEOを両立します。
Astro 5.1 On-Demand Rendering とは何か
Astro 5.1(2026年5月リリース)では、ページ単位で静的生成(SSG)と動的レンダリング(SSR)を切り替える On-Demand Rendering の機能が強化されました。
従来のフレームワークでは「全ページSSG」か「全ページSSR」の二択を迫られることが多く、動的コンテンツが一部だけ必要な場合でもサイト全体をSSRにする必要がありました。
Astro 5.1 では、export const prerender = false をページファイルに追加するだけで、そのページだけをリクエスト時にレンダリングするSSRモードに切り替えられます。その他のページはビルド時に静的生成され、CDNから高速配信されます。
この「良いとこ取り」のアプローチにより、以下が実現できます。
- 静的コンテンツ(ブログ記事・ランディングページ)はSSGで配信し、LCPを最小化
- 動的コンテンツ(ユーザーダッシュボード・ログイン後ページ)はSSRで生成し、SEOとパーソナライズを両立
- ビルド時間を短縮(動的ページをビルド対象から除外)
この記事では、On-Demand Rendering の実装方法、ユースケース、パフォーマンス最適化のベストプラクティスを実例とともに解説します。
On-Demand Rendering の基本実装
プロジェクト構成例
src/
├── pages/
│ ├── index.astro # 静的ページ(SSG)
│ ├── blog/
│ │ └── [slug].astro # 静的ページ(SSG)
│ ├── dashboard/
│ │ └── index.astro # 動的ページ(SSR)← export const prerender = false
│ └── api/
│ └── user.ts # エンドポイント(SSR)← export const prerender = false
└── layouts/
└── Layout.astro
SSRモードへの切り替え方法
動的ページの例(src/pages/dashboard/index.astro):
---
export const prerender = false; // この行でSSRモードに切り替え
import Layout from '../../layouts/Layout.astro';
// リクエストごとに実行される処理
const cookie = Astro.request.headers.get('cookie');
const userId = parseUserIdFromCookie(cookie);
// データベースから最新データを取得
const userData = await fetch(`https://api.example.com/users/${userId}`).then(r => r.json());
---
<Layout title="ダッシュボード">
<h1>こんにちは、{userData.name}さん</h1>
<p>最終ログイン: {new Date(userData.lastLogin).toLocaleString('ja-JP')}</p>
</Layout>
静的ページの例(src/pages/index.astro):
---
// prerender の指定がない場合、デフォルトで静的生成(SSG)
import Layout from '../layouts/Layout.astro';
---
<Layout title="ホーム">
<h1>ようこそ</h1>
<p>このページはビルド時に生成され、CDNから配信されます</p>
</Layout>
astro.config.mjs の設定
On-Demand Rendering を使うには、adapter(デプロイ先のサーバーレス環境)を設定する必要があります。
Vercel の例:
import { defineConfig } from 'astro/config';
import vercel from '@astrojs/vercel/serverless';
export default defineConfig({
output: 'hybrid', // SSGとSSRを混在させる
adapter: vercel(),
});
Cloudflare Pages の例:
import { defineConfig } from 'astro/config';
import cloudflare from '@astrojs/cloudflare';
export default defineConfig({
output: 'hybrid',
adapter: cloudflare(),
});
output の選択肢:
| モード | 説明 |
|---|---|
'static' | 全ページSSG(デフォルト) |
'server' | 全ページSSR(prerender = true で個別にSSG化) |
'hybrid' | デフォルトSSG、prerender = false で個別にSSR化 |
推奨: ほとんどのページが静的な場合は output: 'hybrid' を使い、動的ページだけ prerender = false を指定するのが効率的です。
ユースケース別実装パターン
ケース1: 認証が必要なダッシュボード
要件:
- ログイン前のランディングページは静的生成(SEO対策)
- ログイン後のダッシュボードは動的生成(ユーザーごとに異なるデータ表示)
実装:
---
// src/pages/dashboard.astro
export const prerender = false;
import Layout from '../layouts/Layout.astro';
// セッションCookieからユーザーIDを取得
const sessionToken = Astro.cookies.get('session')?.value;
if (!sessionToken) {
return Astro.redirect('/login');
}
// JWTトークンを検証
const { userId } = await verifyToken(sessionToken);
// データベースから最新データを取得
const tasks = await db.query('SELECT * FROM tasks WHERE user_id = $1 ORDER BY created_at DESC', [userId]);
---
<Layout title="ダッシュボード">
<h1>タスク一覧</h1>
<ul>
{tasks.map(task => (
<li>{task.title} - {task.status}</li>
))}
</ul>
</Layout>
この実装のメリット:
/や/featuresなどの静的ページはCDNから即座に配信される(LCP 100ms未満)/dashboardはリクエスト時に最新データを取得し、パーソナライズされたUIを表示- ビルド時にユーザー数分のページを生成する必要がない(ビルド時間短縮)
ケース2: 外部APIのデータを表示するページ
要件:
- ブログ記事一覧は静的生成
- 「急上昇中の記事」セクションは外部APIから取得(1時間ごとに更新)
実装:
---
// src/pages/trending.astro
export const prerender = false;
import Layout from '../layouts/Layout.astro';
// 外部APIから最新のトレンドデータを取得
const trendingPosts = await fetch('https://analytics.example.com/api/trending?period=1h', {
headers: {
'Authorization': `Bearer ${import.meta.env.ANALYTICS_API_KEY}`,
},
}).then(r => r.json());
---
<Layout title="急上昇中の記事">
<h1>いま読まれている記事</h1>
<ul>
{trendingPosts.map(post => (
<li>
<a href={`/blog/${post.slug}`}>{post.title}</a>
<span>({post.views.toLocaleString()} view)</span>
</li>
))}
</ul>
<p>データ更新日時: {new Date().toLocaleString('ja-JP')}</p>
</Layout>
この実装のメリット:
- ビルド時にAPIキーを埋め込まずに済む(セキュリティ向上)
- キャッシュ戦略をサーバーレス関数のレベルで制御可能(後述)
ケース3: A/Bテスト・パーソナライゼーション
要件:
- ユーザーごとに異なるCTAボタンを表示
- Cookieまたはヘッダー情報に基づいてバリアントを切り替え
実装:
---
// src/pages/pricing.astro
export const prerender = false;
import Layout from '../layouts/Layout.astro';
import CTAButtonA from '../components/CTAButtonA.astro';
import CTAButtonB from '../components/CTAButtonB.astro';
// A/Bテストのバリアント判定
const variant = Astro.cookies.get('ab_test_variant')?.value || (Math.random() > 0.5 ? 'A' : 'B');
// Cookieに保存(次回訪問時も同じバリアントを表示)
Astro.cookies.set('ab_test_variant', variant, {
maxAge: 60 * 60 * 24 * 30, // 30日
path: '/',
});
---
<Layout title="料金プラン">
<h1>料金プラン</h1>
{variant === 'A' ? <CTAButtonA /> : <CTAButtonB />}
</Layout>
パフォーマンス最適化のベストプラクティス
1. Edge Caching の活用
動的ページでも、レスポンスに Cache-Control ヘッダーを付与することで、CDN(Vercel Edge、Cloudflare CDN)でキャッシュできます。
例: 1時間キャッシュする:
---
export const prerender = false;
Astro.response.headers.set('Cache-Control', 's-maxage=3600, stale-while-revalidate=86400');
const trendingPosts = await fetch('https://analytics.example.com/api/trending?period=1h').then(r => r.json());
---
<h1>急上昇中の記事</h1>
<!-- 省略 -->
s-maxage=3600: CDNは3600秒(1時間)キャッシュする
stale-while-revalidate=86400: キャッシュ期限切れ後も古いコンテンツを返しつつ、バックグラウンドで再検証
2. データ取得の並列化
複数のAPIを呼ぶ場合、Promise.all() で並列化し、待ち時間を短縮します。
非効率な例(直列実行):
const user = await fetch('/api/user').then(r => r.json());
const tasks = await fetch('/api/tasks').then(r => r.json());
const notifications = await fetch('/api/notifications').then(r => r.json());
効率的な例(並列実行):
const [user, tasks, notifications] = await Promise.all([
fetch('/api/user').then(r => r.json()),
fetch('/api/tasks').then(r => r.json()),
fetch('/api/notifications').then(r => r.json()),
]);
3. クライアント側の部分的ハイドレーション
動的ページでも、静的な部分は初期HTMLに含め、インタラクティブな部分だけJSでハイドレートすることで、Time to Interactive(TTI)を短縮できます。
例: 静的なヘッダー + 動的なタスクリスト:
---
export const prerender = false;
import Header from '../components/Header.astro'; // 静的コンポーネント
import TaskList from '../components/TaskList.jsx'; // Reactコンポーネント
const tasks = await fetchTasks();
---
<Layout>
<Header /> <!-- ハイドレーション不要 -->
<TaskList client:load tasks={tasks} /> <!-- クライアント側でハイドレート -->
</Layout>
ハイドレーションディレクティブの選択:
| ディレクティブ | タイミング |
|---|---|
client:load | ページロード時(優先度高) |
client:idle | ブラウザがアイドル状態になったとき |
client:visible | 要素が画面に入ったとき(Intersection Observer) |
client:media | メディアクエリが一致したとき |
client:only | サーバー側でレンダリングせず、クライアント側のみ |
推奨: ファーストビューに不要なUIは client:visible で遅延ロードし、初期JSバンドルを削減します。
4. ISR(Incremental Static Regeneration)風の実装
AstroにはNext.jsのような組み込みISR機能はありませんが、CDNキャッシュ + バックグラウンド再検証で同等の挙動を実現できます。
例: 5分ごとに再検証:
---
export const prerender = false;
Astro.response.headers.set('Cache-Control', 's-maxage=300, stale-while-revalidate=3600');
const posts = await fetch('https://cms.example.com/api/posts').then(r => r.json());
---
<h1>最新記事</h1>
{posts.map(post => <article>{post.title}</article>)}
この設定の挙動:
- 最初のリクエスト時、サーバーレス関数が実行され、CDNにキャッシュされる
- 5分以内のリクエストは、CDNから即座にレスポンスが返る
- 5分経過後の最初のリクエストでは、古いキャッシュを返しつつ、バックグラウンドでサーバーレス関数を再実行
- 次のリクエストから新しいキャッシュが配信される
この方式により、ユーザーは常に高速なレスポンスを受け取りつつ、コンテンツは定期的に更新されます。
On-Demand Rendering のワークフロー図
flowchart TD
A[クライアントがページをリクエスト] --> B{ページの種類は?}
B -->|静的ページ<br/>prerender未指定| C[CDNからHTMLを配信<br/>LCP < 100ms]
B -->|動的ページ<br/>prerender = false| D[サーバーレス関数を実行]
D --> E{CDNキャッシュは有効?}
E -->|Yes<br/>Cache-Control設定あり| F[CDNにキャッシュを保存]
E -->|No| G[毎回サーバーレス関数実行]
F --> H[次回以降はCDNから配信]
G --> I[レスポンス返却]
H --> I
C --> I
図の解説:
- 静的ページ(
prerender指定なし)はビルド時に生成され、CDNから配信されるため、LCPは100ms未満 - 動的ページ(
prerender = false)はリクエストごとにサーバーレス関数が実行される Cache-Controlヘッダーを設定すると、動的ページもCDNでキャッシュされ、次回以降は高速配信される
注意点とトラブルシューティング
1. adapter 未設定のエラー
エラーメッセージ:
Error: Cannot use `prerender: false` without an adapter.
解決策: astro.config.mjs に adapter を追加してください(前述の設定例を参照)。
2. 環境変数がサーバーレス関数で使えない
原因: ビルド時の環境変数(PUBLIC_ プレフィックス)とランタイムの環境変数は異なります。
解決策: デプロイ先のプラットフォーム(Vercel、Cloudflare Pages)で環境変数を設定し、import.meta.env で参照します。
Vercel の例:
const apiKey = import.meta.env.API_KEY; // Vercelのダッシュボードで設定
3. ビルド時間が長い
原因: output: 'server' にすると、すべてのページがビルド対象から除外され、逆に動的ページが多すぎるとビルドが遅くなります。
解決策:
output: 'hybrid'を使い、静的ページはビルド時に生成する- 動的ページの数を最小限にし、データ取得をAPIエンドポイント化する
4. SEOへの影響
懸念: 動的ページは初期HTMLにコンテンツが含まれるか?
回答: 含まれます。Astro の SSR はサーバー側でHTMLを生成するため、検索エンジンのクローラーは完全なHTMLを受け取ります。これはクライアント側レンダリング(CSR)と異なる点です。
検証方法:
curl https://yoursite.com/dashboard | grep "<h1>"
サーバー側でレンダリングされている場合、<h1> タグが含まれたHTMLが返ります。
まとめ
Astro 5.1 の On-Demand Rendering により、以下が実現できます。
- 静的ページと動的ページを混在させ、パフォーマンスとSEOを両立
export const prerender = falseの一行で簡単に切り替え可能- CDNキャッシュと組み合わせることで、ISR風の挙動を実現
- 認証、A/Bテスト、外部API連携など、幅広いユースケースに対応
ポイント:
- ほとんどのページが静的な場合は
output: 'hybrid'を使う - 動的ページにも
Cache-Controlヘッダーを設定し、CDNキャッシュを活用する - データ取得は並列化し、サーバーレス関数の実行時間を短縮する
- 初期HTMLに静的コンテンツを含め、インタラクティブな部分だけクライアント側でハイドレートする
この機能により、従来は「全ページSSR」にせざるを得なかったサイトも、ページ単位で最適なレンダリング方式を選択できるようになりました。