はじめに/ガイド/ISR

インクリメンタル静的再生成 (ISR) の実装方法

インクリメンタル静的再生成 (ISR) を使用すると、以下のことが可能です:

  • サイト全体を再ビルドせずに静的コンテンツを更新
  • 事前レンダリングされた静的ページを提供することでサーバー負荷を軽減
  • 適切な cache-control ヘッダーが自動的にページに追加されることを保証
  • 大量のコンテンツページを next build の長時間化なしに処理

以下は最小限の例です:

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
  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 はオンデマンドでこのページを生成してキャッシュ

リファレンス

ルートセグメント設定

関数

時間ベースの再検証

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

interface Post {
  id: string
  title: string
  content: string
}

export const revalidate = 3600 // 1時間ごとに無効化

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

1秒ではなく1時間など、長めの再検証時間を設定することを推奨します。より精密な制御が必要な場合は、オンデマンド再検証を検討してください。リアルタイムデータが必要な場合は、動的レンダリングへの切り替えを検討してください。

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

より精密な再検証方法として、revalidatePath 関数でオンデマンドにページを無効化できます。

例えば、新しい投稿を追加した後にこのサーバーアクションが呼び出されます。サーバーコンポーネントで fetch を使用するかデータベースに接続するかにかかわらず、この操作はルート全体のキャッシュをクリアし、サーバーコンポーネントが新しいデータを取得できるようにします。

'use server'

import { revalidatePath } from 'next/cache'

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

デモを表示 および ソースコードを確認

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

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

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 を使用できます:

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()
  // ...
}

その後、サーバーアクション または ルートハンドラーrevalidateTag を使用できます:

'use server'

import { revalidateTag } from 'next/cache'

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

未捕捉例外の処理

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

キャッシュロケーションのカスタマイズ

キャッシュされたページとデータを永続ストレージに保持したり、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はサポートされません
  • 静的生成されたルート内で複数のfetchリクエストがあり、それぞれ異なるrevalidate頻度が設定されている場合、ISRには最も短い時間が使用されます。ただし、各revalidate頻度はデータキャッシュによって尊重されます
  • ルートで使用されるfetchリクエストのいずれかがrevalidate時間0、または明示的なno-storeを持つ場合、そのルートは動的レンダリングされます
  • オンデマンドISRリクエストではミドルウェアは実行されません。つまり、パス書き換えやミドルウェア内のロジックは適用されません。正確なパスを再検証していることを確認してください。例: 書き換えられた/post-1ではなく/post/1

プラットフォームサポート

デプロイ方法サポート状況
Node.jsサーバーはい
Dockerコンテナはい
静的エクスポートいいえ
アダプタープラットフォーム依存

Next.jsをセルフホスティングする際のISR設定方法について学べます。

バージョン履歴

バージョン変更内容
v14.1.0カスタムcacheHandlerが安定版に
v13.0.0App Routerが導入
v12.2.0Pages Router: オンデマンドISRが安定版に
v12.0.0Pages Router: ボット対応ISRフォールバック追加
v9.5.0Pages Router: 安定版ISR導入