メインコンテンツまでスキップ

Incremental Static Regeneration (ISR)

Incremental Static Regeneration (ISR)を使用すると、次のことが可能になります:

  • サイト全体を再構築することなく静的コンテンツを更新する
  • ほとんどのリクエストに対して事前レンダリングされた静的ページを提供することでサーバー負荷を軽減する
  • 適切なcache-controlヘッダーがページに自動的に追加されることを保証する
  • 長いnext build時間をかけずに大量のコンテンツページを処理する

以下は最小限の例です:

app/blog/[id]/page.tsx
interface Post {
id: string
title: string
content: string
}

// Next.jsは、リクエストが来たときにキャッシュを無効にします。
// ただし、最大で60秒に1回です。
export const revalidate = 60

// ビルド時に`generateStaticParams`からのパラメータのみを事前レンダリングします。
// 生成されていないパスに対してリクエストが来た場合、
// Next.jsはオンデマンドでページをサーバーレンダリングします。
export const dynamicParams = true // またはfalseにして、未知のパスで404を返す

export async function generateStaticParams() {
const posts: Post[] = await fetch('https://api.vercel.app/blog').then((res) =>
res.json()
)
return posts.map((post) => ({
id: String(post.id),
}))
}

export default async function Page({
params,
}: {
params: Promise<{ id: string }>
}) {
const id = (await params).id
const post: Post = await fetch(`https://api.vercel.app/blog/${id}`).then(
(res) => res.json()
)
return (
<main>
<h1>{post.title}</h1>
<p>{post.content}</p>
</main>
)
}

この例がどのように機能するかを説明します:

  1. next build中に、すべての既知のブログ投稿が生成されます(この例では25件あります)
  2. これらのページへのすべてのリクエスト(例:/blog/1)はキャッシュされ、即座に応答します
  3. 60秒が経過した後、次のリクエストはキャッシュされた(古い)ページを表示します
  4. キャッシュが無効になり、新しいバージョンのページがバックグラウンドで生成され始めます
  5. 正常に生成されると、Next.jsは更新されたページを表示し、キャッシュします
  6. /blog/26がリクエストされた場合、Next.jsはこのページをオンデマンドで生成し、キャッシュします

参考

Route segment config

関数

時間ベースの再検証

これは、/blogでブログ投稿のリストを取得して表示します。1時間後、このページのキャッシュは次回のページ訪問時に無効になります。その後、バックグラウンドで最新のブログ投稿を含む新しいバージョンのページが生成されます。

app/blog/page.tsx
interface Post {
id: string
title: string
content: string
}

export const revalidate = 3600 // 毎時無効化

export default async function Page() {
const data = await fetch('https://api.vercel.app/blog')
const posts: Post[] = await data.json()
return (
<main>
<h1>Blog Posts</h1>
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</main>
)
}

高い再検証時間を設定することをお勧めします。たとえば、1秒ではなく1時間です。より正確な再検証が必要な場合は、オンデマンド再検証を検討してください。リアルタイムデータが必要な場合は、動的レンダリングに切り替えることを検討してください。

revalidatePathを使用したオンデマンド再検証

より正確な再検証方法として、revalidatePath関数を使用してオンデマンドでページを無効化します。

たとえば、新しい投稿を追加した後にこのServer Actionが呼び出されます。Server Componentでデータを取得する方法に関係なく、fetchを使用するかデータベースに接続するかにかかわらず、これによりルート全体のキャッシュがクリアされ、Server Componentが新しいデータを取得できるようになります。

app/actions.ts
'use server'

import { revalidatePath } from 'next/cache'

export async function createPost() {
// キャッシュ内の/postsルートを無効化
revalidatePath('/posts')
}

デモを表示し、ソースコードを探索してください。

revalidateTagを使用したオンデマンド再検証

ほとんどのユースケースでは、全体のパスを再検証することをお勧めします。より細かい制御が必要な場合は、revalidateTag関数を使用できます。たとえば、個々のfetch呼び出しにタグを付けることができます:

app/blog/page.tsx
export default async function Page() {
const data = await fetch('https://api.vercel.app/blog', {
next: { tags: ['posts'] },
})
const posts = await data.json()
// ...
}

ORMを使用している場合やデータベースに接続している場合は、unstable_cacheを使用できます:

app/blog/page.tsx
import { unstable_cache } from 'next/cache'
import { db, posts } from '@/lib/db'

const getCachedPosts = unstable_cache(
async () => {
return await db.select().from(posts)
},
['posts'],
{ revalidate: 3600, tags: ['posts'] }
)

export default async function Page() {
const posts = getCachedPosts()
// ...
}

その後、Server ActionsまたはRoute HandlerrevalidateTagを使用できます:

app/actions.ts
'use server'

import { revalidateTag } from 'next/cache'

export async function createPost() {
// キャッシュ内の'posts'タグが付けられたすべてのデータを無効化
revalidateTag('posts')
}

キャッチされない例外の処理

データを再検証しようとしたときにエラーが発生した場合、最後に正常に生成されたデータがキャッシュから提供され続けます。次のリクエストで、Next.jsはデータの再検証を再試行します。エラー処理について詳しく学ぶ

キャッシュの場所をカスタマイズする

ページのキャッシュと再検証(Incremental Static Regenerationを使用)は、同じ共有キャッシュを使用します。Vercelにデプロイする場合、ISRキャッシュは自動的に耐久性のあるストレージに保存されます。

セルフホスティングする場合、ISRキャッシュはNext.jsサーバー上のファイルシステム(ディスク上)に保存されます。これは、PagesとApp Routerの両方を使用してセルフホスティングする場合に自動的に機能します。

キャッシュされたページとデータを耐久性のあるストレージに保存したり、Next.jsアプリケーションの複数のコンテナやインスタンス間でキャッシュを共有したい場合は、Next.jsキャッシュの場所を設定できます。詳細はこちら

トラブルシューティング

ローカル開発でキャッシュされたデータをデバッグする

fetch APIを使用している場合、キャッシュされたリクエストとキャッシュされていないリクエストを理解するために追加のログを追加できます。loggingオプションについて詳しく学ぶ

next.config.js
module.exports = {
logging: {
fetches: {
fullUrl: true,
},
},
}

正しい本番動作を確認する

本番環境でページが正しくキャッシュされ、再検証されていることを確認するには、next buildを実行し、その後next startを実行して本番Next.jsサーバーをローカルでテストできます。

これにより、本番環境でのISRの動作をテストできます。さらにデバッグするために、次の環境変数を.envファイルに追加します:

.env
NEXT_PRIVATE_DEBUG_CACHE=1

これにより、Next.jsサーバーのコンソールにISRキャッシュのヒットとミスがログ出力されます。next build中に生成されたページや、パスがオンデマンドでアクセスされるときにページがどのように更新されるかを確認できます。

注意事項

  • ISRはNode.jsランタイム(デフォルト)を使用する場合にのみサポートされています。
  • ISRはStatic Exportを作成する場合にはサポートされていません。
  • 静的にレンダリングされたルートに複数のfetchリクエストがあり、それぞれ異なるrevalidate頻度を持つ場合、ISRには最も低い時間が使用されます。ただし、これらの再検証頻度はData Cacheによって引き続き尊重されます。
  • ルートで使用されるfetchリクエストのいずれかがrevalidate時間を0に設定している場合、または明示的にno-storeを指定している場合、そのルートは動的にレンダリングされます。
  • オンデマンドISRリクエストにはミドルウェアが実行されないため、ミドルウェア内のパスの書き換えやロジックは適用されません。正確なパスを再検証していることを確認してください。たとえば、書き換えられた/post-1ではなく、/post/1です。

バージョン履歴

バージョン変更点
v14.1.0カスタムcacheHandlerが安定しました。
v13.0.0App Routerが導入されました。
v12.2.0Pages Router: オンデマンドISRが安定しました
v12.0.0Pages Router: Bot-aware ISR fallbackが追加されました。
v9.5.0Pages Router: 安定したISRが導入されました