言語を切り替える
テーマを切り替える

Next.js キャッシュメカニズム完全ガイド:revalidate の正しい使用タイミングを習得する

午前1時。私は画面上の忌々しい「古いデータ」を睨みつけ、もう20回もリロードを繰り返していました。10分前にデータベースでタイトルを手動変更したのに、ページは更新されません。コードを開くと、revalidate: 60 とはっきり書いてある。怒りに任せて revalidate: 10 に変更。サーバー再起動。リロード。それでも古いまま。

この絶望感、分かりますか?

正直なところ、Next.js のキャッシュメカニズムはフレームワーク全体の中で最も人を狂わせる部分かもしれません。4層のキャッシュ、3つの revalidate メソッド、そしてバージョン 14 から 15 への破壊的変更。revalidate: 60 を設定すれば終わりだと思いましたか? いえ、Router Cache が悪さをしているかもしれないし、Full Route Cache が無効になっていないかもしれないし、あるいは単に開発環境でテストしているだけかもしれません。

この記事では、「キャッシュはパフォーマンス向上の重要な手段です」といった正しいけれど退屈な話はしません。最も率直な方法で、以下を伝えます:

  • Next.js の4層キャッシュとは何か、各層の役割は?
  • revalidatePath、revalidateTag、updateTag の違いと使い分け
  • データが更新されない時、どうやって層ごとに原因を特定するか

もしあなたも、データが更新されない、revalidate が効かない、どの API を使うべきか分からないという状況に陥ったことがあるなら、これからの12分で数日分の徹夜時間を節約できるかもしれません。

なぜ Next.js のキャッシュはこんなに複雑なのか

なぜこれほど多くの層が必要なのか?

正直に言って、初めて Next.js に4層のキャッシュがあると知った時、私も今のあなたと同じような顔をしていました。Request Memoization? Full Route Cache? 何それ? もっとシンプルにできないの?

しかし冷静に考えると、各層のキャッシュはそれぞれ異なる環境下のパフォーマンス問題を解決しています:

  • コンポーネントツリー内で10個のコンポーネントがユーザー情報を取得する場合、リクエストを10回飛ばすわけにはいきませんよね?
  • ブログ記事一覧が1日に数回しか更新されないなら、アクセスのたびに再レンダリングするのは無駄ですよね?
  • ユーザーが「戻る」ボタンを押した時、ページ再読み込みを待たせるわけにはいきませんよね?

各層にはそれぞれの役割があります。問題は、それらが相互に影響し合うことです。データを変更したのに、どのキャッシュをクリアすればいいのか分からないのはそのためです。

Next.js 14 vs 15:キャッシュ革命

2024年末、Next.js 15 は大きなニュースを発表しました:デフォルトで fetch リクエストをキャッシュしなくなりました。

以前(14):

fetch(url) // デフォルトでキャッシュ, cache: 'force-cache' と同等

現在(15):

fetch(url) // デフォルトでキャッシュしない, cache: 'no-store' と同等

この変更により、アップグレード後にパフォーマンスが急激に低下したと感じる人が続出しました。元々自動的にキャッシュされていたデータがキャッシュされなくなったからです。Vercel のフォーラムは大荒れでしたが、公式の理由は「明示的であることは暗黙的であるより良い。キャッシュは開発者が能動的に選択すべきもので、デフォルトの挙動であるべきではない」というものでした。

理屈は通っていますが、既存プロジェクトにとっては破壊的な変更です。

4層キャッシュのパノラマ

簡単に言えば、データがサーバーからユーザーのブラウザに届くまで、以下の4層を経由します:

  1. Request Memoization(リクエスト記憶化)
    スコープ:単一リクエストのレンダリングサイクル
    管理者:React

  2. Data Cache(データキャッシュ)
    スコープ:サーバー、リクエスト間で永続化
    管理者:Next.js

  3. Full Route Cache(完全ルートキャッシュ)
    スコープ:サーバー、静的ルート
    管理者:Next.js

  4. Router Cache(ルーターキャッシュ)
    スコープ:クライアントブラウザのメモリ
    管理者:Next.js

データの流れはおおよそ以下のようになります:

ユーザーアクセス → Router Cache(クライアント)→ Full Route Cache(サーバー)

                                  Data Cache → Request Memoization → データソース

あなたの revalidate は主に Data Cache と Full Route Cache に影響します。一方、Router Cache をクリアするには router.refresh() かハードリロードが必要です。

これが、revalidate したはずなのにクライアントのリロードで古いデータが表示される理由です。Router Cache が残っているからです。

次章では、これら4つのキャッシュを一層ずつ分解し、それぞれが何をしているのか、いつ有効になり、いつ無効になるのかを解説します。

4層キャッシュメカニズム詳細

Request Memoization(リクエスト記憶化)

これは何?

これは実は React 18 の機能で、Next.js 独自のものではありません。簡単に言うと、1回のレンダリングサイクル内で複数のコンポーネントが同じ GET リクエストを送信した場合、React が自動的にそれらを1回のリクエストに統合します。

例:

// app/page.tsx
async function UserProfile() {
  const user = await fetch('https://api.example.com/user/123')
  return <div>{user.name}</div>
}

async function UserAvatar() {
  const user = await fetch('https://api.example.com/user/123')  // 同じリクエスト
  return <img src={user.avatar} />
}

export default function Page() {
  return (
    <>
      <UserProfile />
      <UserAvatar />
    </>
  )
}

これら2つのコンポーネントは同じ URL をリクエストしていますが、実際には1回しかリクエストが飛びません。React は最初の結果を記憶し、2回目はキャッシュを使用します。

超短命なライフサイクル

このキャッシュは単一のレンダリングプロセス中のみ有効です。レンダリングが終わればキャッシュは消えます。次にユーザーがページをリロードすれば、また新しいリクエストになります。

注意点

  • サーバーコンポーネント(Server Component)でのみ有効
  • GET リクエストでのみ有効(POST, PUT 等は記憶されない)
  • 開発環境では効果が見えない場合があります(コード変更のたびに再レンダリングされるため)

いつ使う?

おそらく意識する必要はありません。これは React が自動的に行う最適化で、手動制御はできません。言及したのは、「複数のコンポーネントで同じ fetch を書いても重複リクエストの心配はない」と伝えるためです。

Data Cache(データキャッシュ)

これこそが重要

Data Cache は Next.js キャッシュメカニズムの中核です。fetch リクエストの結果をサーバーのファイルシステムに保存し、リクエスト間、ユーザー間、デプロイ間で永続化させます。

Next.js 14 vs 15 の雲泥の差

Next.js 14:

fetch('https://api.example.com/posts')
// 以下と同等
fetch('https://api.example.com/posts', { cache: 'force-cache' })
// 結果:手動で revalidate しない限り、データは永久にキャッシュされる

Next.js 15:

fetch('https://api.example.com/posts')
// 以下と同等
fetch('https://api.example.com/posts', { cache: 'no-store' })
// 結果:毎回再リクエスト、キャッシュしない

データをキャッシュしたい場合(Next.js 15)

方法1:単一リクエストで設定

fetch('https://api.example.com/posts', {
  cache: 'force-cache',
  next: { revalidate: 3600 }  // 1時間後に再検証
})

方法2:ルート全体で設定

// app/blog/page.tsx
export const revalidate = 3600

export default async function BlogPage() {
  const posts = await fetch('https://api.example.com/posts')
  // ...
}

いつ無効になる?

Data Cache が無効になるタイミングは3つ:

  1. 設定した revalidate 時間が経過した(例:3600秒)
  2. 手動で revalidatePath() または revalidateTag() を呼び出した
  3. アプリを再デプロイした(Data Cache の設定によるが、通常はクリアされる場合が多い)

複数の fetch で revalidate 時間が違う場合は?

1つのページに複数の fetch があり、それぞれ revalidate 時間が異なる場合、Next.js は最も短い時間をページ全体の再検証時間として採用します。

async function Page() {
  const posts = await fetch('...', { next: { revalidate: 60 } })    // 60秒
  const user = await fetch('...', { next: { revalidate: 3600 } })  // 1時間

  // 実際には、ページ全体が60秒ごとに再検証される
}

Full Route Cache(完全ルートキャッシュ)

HTML レベルのキャッシュ

Data Cache がデータをキャッシュするなら、Full Route Cache はページ全体の HTML と RSC Payload(React Server Component のシリアライズデータ)をキャッシュします。

いつキャッシュされる?

静的レンダリングされるルートのみキャッシュされます。静的レンダリングとは? ビルド時に内容を確定できるページのことです。

逆に、ページで以下を使用すると動的レンダリングとなり、キャッシュされません:

  • cookies()
  • headers()
  • searchParams
  • 不安定な関数(Math.random()Date.now() など)

ページが静的か動的かの判断方法

npm run build を実行すると、ターミナルに表示されます:

Route (app)                              Size     First Load JS
┌ ○ /                                    5 kB           87 kB
├ ● /blog                                1 kB           88 kB
└ ƒ /api/user                            0 kB           87 kB

○  (Static)  静的HTMLとして自動レンダリング(動的データなし)
●  (SSG)     静的HTML + JSONとして自動生成(getStaticProps使用)
ƒ  (Dynamic) サーバーサイドでオンデマンドレンダリング

なら静的でキャッシュされます。ƒ なら動的でキャッシュされません。

静的・動的の強制

// 静的を強制
export const dynamic = 'force-static'

// 動的を強制
export const dynamic = 'force-dynamic'

いつ無効になる?

Full Route Cache の無効化タイミング:

  1. Data Cache が無効になった時(データが変わればページも再レンダリングが必要だから)
  2. revalidatePath('/blog') を呼び出した時
  3. アプリを再デプロイした時

Router Cache(ルーターキャッシュ)

クライアント側の小細工

Router Cache はユーザーのブラウザメモリ内に存在するキャッシュです。ユーザーがページを訪問すると、Next.js はそのページの内容をクライアント側にキャッシュします。次回ユーザーが「戻る」ボタンを押したり、そのページに移動したりすると、サーバーにリクエストせずキャッシュを使用します。

プリフェッチという裏技

<Link href="/about"> を使用すると、リンクがユーザーの視界に入った時点で、Next.js は /about ページの内容を自動的にプリフェッチし、Router Cache に保存します。ユーザーが実際にクリックした瞬間、即座に遷移できます。

ライフサイクル(Next.js 14)

  • 静的ルート:5分間キャッシュ
  • 動的ルート:30秒間キャッシュ

Next.js 15 での変更点

Next.js 15 はデフォルトで Router Cache を有効にしません(あるいはキャッシュ時間が非常に短いです)。有効にしたい場合は next.config.js で設定が必要です:

// next.config.js
module.exports = {
  experimental: {
    staleTimes: {
      dynamic: 30,      // 動的ルート30秒
      static: 180,      // 静的ルート180秒
    },
  },
}

いつ無効になる?

  • キャッシュ時間が経過した
  • ユーザーがハードリロード(Ctrl+Shift+R)した
  • router.refresh() を呼び出した

なぜ revalidate が効かないように見えるのか?

多くの場合、サーバー側で revalidatePath を呼び出してデータは更新されているのに、ユーザーがリロードしても古いデータが見えるのは、Router Cache がまだ有効だからです。

解決策:

  1. ハードリロード(ユーザーに強要はできませんが)
  2. データ更新後に router.refresh() を呼び出す(クライアントコンポーネントの場合)
  3. Router Cache の時間を短くする

revalidate メソッド全解析

さて、4層キャッシュの原理は分かりました。ここからは最も実用的な部分、どうやってキャッシュを無効にするかです。

Next.js はいくつかの revalidate メソッドを提供しており、それぞれ適用シーンが異なります。違いを理解すれば、デバッグ時間を大幅に節約できます。

時間ベースの revalidate(Time-based)

最も一般的な方法

時間ベースの revalidate は、「X秒ごとにデータを自動再取得する」というものです。これが ISR(Incremental Static Regeneration、増分静的再生成)の核心メカニズムです。

2つの書き方

方法1:fetch リクエスト内で設定

const res = await fetch('https://api.example.com/posts', {
  next: { revalidate: 3600 }  // 3600秒 = 1時間
})

方法2:ルートファイルのトップレベルで設定

// app/blog/page.tsx
export const revalidate = 3600

export default async function BlogPage() {
  const posts = await fetch('https://api.example.com/posts')
  return <PostList posts={posts} />
}

動作原理(ISR)

revalidate: 3600 を設定したと仮定します:

  1. 最初のユーザーがページ訪問 → 静的 HTML 生成、1時間キャッシュ
  2. 続く1時間、全ユーザーはそのキャッシュされた HTML を見る(超高速)
  3. 1時間後、次のユーザーが訪問 → まだ古い HTML を返す(待たせない)
  4. しかし同時に、Next.js はバックグラウンドで新しい HTML を再生成する
  5. 新 HTML 生成完了後、それ以降のユーザーは新しいコンテンツを見る

このメカニズムを stale-while-revalidate(再検証中は古いコンテンツを返す)と呼びます。メリットはユーザーを待たせないこと、デメリットは常に1人のユーザーが古いデータを見ることになる点です。

適用シーン

  • ブログ記事一覧(1時間に数回更新)
  • ニューストップページ(30分ごとに更新)
  • 商品カタログ(1日1回更新)

よくある問題1:開発環境で効かない

revalidate 設定したのに効かない」という不満をよく聞きます。最初に確認すべきは、開発環境と本番環境どちらでテストしているか? です。

開発環境(npm run dev)では、Next.js はほとんどのキャッシュを無効にし、リクエストごとに再レンダリングします。本番環境でテストする必要があります:

npm run build
npm start

よくある問題2:複数の fetch の時間が不一致

前述の通り、ページ内に複数の fetch があり revalidate 時間が異なる場合、Next.js は最小値を取ります。しかし詳細があります:Data Cache は各 fetch 独自の時間に従います

async function Page() {
  // このリクエストは60秒ごとに再検証
  const posts = await fetch('...', { next: { revalidate: 60 } })

  // このリクエストは3600秒ごとに再検証
  const user = await fetch('...', { next: { revalidate: 3600 } })
}

ページは60秒ごとに再レンダリングされますが、user リクエストのデータキャッシュは1時間保持されます。つまり、最初の1時間は60秒ごとにページが再構築されても user データは変わらず、1時間後にようやく user データが更新されます。

ややこしいですが、合理的設計です。

オンデマンド revalidate:revalidatePath

ユーザー主導の更新

時間ベースがタイマーなら、revalidatePath はスイッチです。特定のイベント(例:ユーザーが記事を投稿)が発生した時、手動でキャッシュを無効化します。

使用方法

// app/actions.ts
'use server'

import { revalidatePath } from 'next/cache'

export async function publishPost(formData) {
  // 記事投稿ロジック
  await db.posts.create({ ... })

  // ブログ一覧ページのキャッシュを無効化
  revalidatePath('/blog')
}

クライアントコンポーネントで呼び出し:

// app/components/PublishButton.tsx
'use client'

import { publishPost } from '@/app/actions'

export function PublishButton() {
  return (
    <form action={publishPost}>
      <button type="submit">記事を公開</button>
    </form>
  )
}

パスタイプの指定

revalidatePath はパスの種類を指定できます:

// /blog このページだけ再検証
revalidatePath('/blog', 'page')

// /blog 以下の全ページ(/blog/post-1, /blog/post-2 含む)を再検証
revalidatePath('/blog', 'layout')

重要:即時再生成ではない

多くの人が revalidatePath を呼べば即座にページが再生成されると思っていますが、違います

revalidatePath はキャッシュを期限切れとしてマークするだけです。実際の再生成は、次にユーザーがそのページにアクセスした時に発生します。

つまり:

  1. あなたが revalidatePath('/blog') を呼ぶ
  2. キャッシュがクリアされる
  3. 次のユーザーが /blog を訪問 → この時初めて HTML 再生成(ユーザーは待つことになる)
  4. 以降のユーザーは新しいコンテンツを見る

適用シーン

  • ユーザーがコンテンツ投稿後に一覧を更新
  • 管理者が設定変更後に関連ページを更新
  • フォーム送信後に現在のページを更新

オンデマンド revalidate:revalidateTag

より柔軟な無効化制御

revalidatePath がパス単位なら、revalidateTagタグ単位です。データにタグを付け、そのタグが付いた全キャッシュを一括で無効化できます。

使用方法

ステップ1:fetch リクエストにタグ付け

const posts = await fetch('https://api.example.com/posts', {
  next: {
    revalidate: 3600,
    tags: ['posts']  // 'posts' タグを付与
  }
})

const authors = await fetch('https://api.example.com/authors', {
  next: {
    revalidate: 3600,
    tags: ['posts', 'authors']  // 複数タグも可
  }
})

ステップ2:特定タグのキャッシュを無効化

'use server'

import { revalidateTag } from 'next/cache'

export async function publishPost() {
  await db.posts.create({ ... })

  // 'posts' タグが付いた全データが無効化される
  revalidateTag('posts')
}

profile=“max” の stale-while-revalidate 戦略

Next.js 15 では profile="max" の使用が推奨されます:

revalidateTag('posts', { profile: 'max' })

profile="max" 使用時:

  1. 期限切れとマークするが、キャッシュを即時削除しない
  2. 次のアクセス時 → 古いデータを返す(爆速!)
  3. 同時にバックグラウンドで新データを取得
  4. 新データ準備完了後、以降のリクエストは新データを返す

これはデフォルトの挙動より優れています。ユーザーを待たせないからです。

revalidatePath vs revalidateTag の違い

比較項目revalidatePathrevalidateTag
無効化粒度パス単位データタグ単位
ページまたぎ具体的なパス指定が必要複数ページにまたがって可能
精細度粗い細かい
複雑さ簡単タグ設計が必要

いつ Tag を使うべき?

データが複数のページで使われている場合、Tag が適しています。

例えばブログシステム:

  • 記事一覧 /blog
  • 記事詳細 /blog/[slug]
  • 著者ページ /author/[id]
  • トップページの「最新記事」モジュール

これら4ページすべてが「記事データ」を使用しています。revalidatePath ならこうなります:

revalidatePath('/blog')
revalidatePath('/blog/[slug]')
revalidatePath('/author/[id]')
revalidatePath('/')

しかし記事データに posts タグを付けていれば:

revalidateTag('posts')

これだけで済みます。圧倒的に楽です。

新機能:updateTag(Next.js 15)

遅延ではなく、即時無効化

updateTag は Next.js 15 の新 API で、revalidateTag との違いは、キャッシュを即時削除し、期限切れマークではない点です。

'use server'

import { updateTag } from 'next/cache'

export async function updateUserProfile(userId, newData) {
  await db.users.update({ where: { id: userId }, data: newData })

  // ユーザーデータキャッシュを即時無効化
  updateTag(`user-${userId}`)
}

revalidateTag との違い

比較項目revalidateTagupdateTag
無効化方式期限切れマーク、次回アクセス時にバックグラウンド更新キャッシュ即時削除
次回アクセス古いデータを返し、裏で取得待機し、新データを取得
使用制限どこでも使用可Server Actions 内のみ
適用シーン一般的なシーン、速度重視”Read-your-writes”(自分の書き込みを読む)シーン

“Read-your-writes”とは?

ユーザーがプロフィール画面でニックネームを変更し保存ボタンを押した後、ページには即座に新しいニックネームが表示されるべきです。古いのが表示されて(バックグラウンド更新を待つ)は困ります。

こういうシーンで updateTag を使います:

export async function updateProfile(formData) {
  const userId = getCurrentUserId()

  await db.users.update({
    where: { id: userId },
    data: { nickname: formData.get('nickname') }
  })

  // 即時無効化し、次回読み込みで最新データを保証
  updateTag(`user-${userId}`)

  revalidatePath('/profile')
}

新機能:use cache ディレクティブ(Next.js 15)

明示的なキャッシュ宣言

Next.js 15 では新たに 'use cache' ディレクティブが導入され、どの関数をキャッシュすべきか明示的にマークできるようになりました。

使用方法

'use cache'

export async function getPopularPosts() {
  const posts = await db.posts.findMany({
    orderBy: { views: 'desc' },
    take: 10
  })
  return posts
}

cacheTag との併用

import { unstable_cacheTag as cacheTag } from 'next/cache'

'use cache'

export async function getPostsByAuthor(authorId) {
  cacheTag('posts', `author-${authorId}`)

  return await db.posts.findMany({
    where: { authorId }
  })
}

その後 revalidateTag で無効化できます:

revalidateTag(`author-${authorId}`)

なぜこれが必要?

Next.js 15 では fetch がデフォルトでキャッシュされないため、毎回 DB クエリを投げたくない場合、明示的にキャッシュを宣言する必要があります。use cache はその意図を明確にします。

よくある問題トラブルシューティング

理論は以上です。ここからは最も実用的なキャッシュトラブル解決法です。

よくある4つの問題と、その調査手順・解決策をリストアップします。

問題1: revalidate を設定したのに効かない

症状

export const revalidate = 60 を設定したのに、10分後にアクセスしてもデータが古いまま。

調査手順

1. 本番環境でのテストか確認

これが最大の落とし穴です。開発環境(npm run dev)はキャッシュの大部分を無効にします。

必ず以下でテストしてください:

npm run build
npm start

2. ルートが動的レンダリングになっていないか確認

npm run build を実行し、出力を見ます:

Route (app)                Size
├ ○ /blog                  1 kB    ← 静的、キャッシュされる
└ ƒ /profile               2 kB    ← 動的、キャッシュされない

ルートが ƒ(動的)なら、revalidate は機能しません。動的ルートはキャッシュされないからです。

動的レンダリングを引き起こすコード:

// これらを使うと動的レンダリングになる
import { cookies } from 'next/headers'
import { headers } from 'next/headers'

export default function Page({ searchParams }) {  // searchParams 使用
  const cookieStore = cookies()  // cookies 使用
  // ...
}

解決策:

  • 動的レンダリング不要なら、それらを除外
  • 必要なら、revalidate は諦めてオンデマンド revalidate を使う

3. Next.js バージョンの確認

Next.js 14 と 15 はデフォルト挙動が異なります。15 に上げたばかりなら、以前キャッシュされていたものがされなくなっている可能性があります。

解決策(Next.js 15):

// 明示的にキャッシュ有効化
fetch(url, {
  cache: 'force-cache',
  next: { revalidate: 60 }
})

// または use cache ディレクティブ
'use cache'
export async function getData() {
  // ...
}

4. Router Cache の干渉確認

サーバーデータは更新されていても、クライアントの Router Cache が古いデータを持っている可能性があります。

解決策:

  • ハードリロード(Ctrl+Shift+R)
  • Next.js 15 で短縮 staleTimes を設定

問題2: データ更新したのにページは古いまま

症状

DB を手動更新したり revalidatePath を呼んだのに、リロードしても古いデータのまま。

調査思考法:4層キャッシュを順にチェック

第1層:Router Cache(クライアント)

クライアントキャッシュが見落とされがちです。

クイックテスト:

  • Ctrl+Shift+R でハードリロード
  • これで更新されるなら Router Cache の問題

解決策:

'use client'

import { useRouter } from 'next/navigation'

export function RefreshButton() {
  const router = useRouter()

  return (
    <button onClick={() => router.refresh()}>
      更新
    </button>
  )
}

または next.config.js でキャッシュ時間を短縮:

module.exports = {
  experimental: {
    staleTimes: {
      dynamic: 0,    // 動的ルートキャッシュ無効
      static: 30,    // 静的ルート30秒
    },
  },
}

第2層:Full Route Cache(サーバー)

ルートが静的か確認。静的なら HTML 全体がキャッシュされています。

クイックテスト:

# 新しいシークレットウィンドウでアクセス
# それでも古ければサーバーキャッシュ

解決策:

'use server'

import { revalidatePath } from 'next/cache'

export async function updateData() {
  // ... データ更新
  revalidatePath('/your-page') // キャッシュ無効化
}

結論

Next.js のキャッシュは凶暴ですが、飼いならせます。

ポイントは以下の通り:

  1. Next.js 15 の破壊的変更を理解する:fetch はデフォルトでキャッシュされなくなりました。
  2. 層を意識する:データ不整合が起きた時、それがクライアント(Router Cache)なのかサーバー(Data Cache/Full Route Cache)なのかを切り分けましょう。
  3. 適切なツールを選ぶ
    • 定期更新 → revalidate: time
    • コンテンツ投稿 → revalidatePath
    • 複雑な依存関係 → revalidateTag
    • 即時反映(入力直後) → updateTag

もしまた深夜1時に「データが更新されない!」と叫びたくなったら、この記事を思い出してください。深呼吸して、Router Cache から順に確認していけば、必ず犯人は見つかります。

FAQ

Next.js 15 で fetch がキャッシュされなくなりました。どうすれば?
Next.js 15 では fetch がデフォルトで `no-store` になりました。キャッシュしたい場合は、明示的に `fetch(url, { cache: 'force-cache' })` を指定するか、新しい `'use cache'` ディレクティブを使用してください。
revalidatePath を呼び出してもページが更新されません。
2つの理由が考えられます。1) クライアントの Router Cache が古いデータを保持している(`router.refresh()` かハードリロードが必要)。2) `revalidatePath` は次回の訪問時に再生成をトリガーするだけで、即座には更新されません(待機が必要です)。即時性を求めるなら `updateTag` を検討してください。
revalidateTag と updateTag の違いは何ですか?
revalidateTag はキャッシュを「期限切れ」とマークし、次回のアクセスで古いデータを返しつつ裏で更新します(stale-while-revalidate)。対して updateTag(Next.js 15+)はキャッシュを即時削除し、次回のアクセスで新しいデータを取得するまで待機させます(Read-your-writes 向け)。
開発環境で revalidate が機能しません。
開発環境(`npm run dev`)では、ホットリロードのために Next.js が意図的にほとんどのキャッシュを無効化しています。キャッシュ動作の正確なテストは、必ず `npm run build` && `npm start` で行った本番ビルドで実施してください。
動的レンダリング(Dynamic Rendering)とは何ですか?
ビルド時に HTML を生成せず、リクエストのたびにサーバーで HTML を生成する方式です。`cookies()`, `headers()`, `searchParams` などの動的関数を使用すると自動的に動的レンダリングになり、Full Route Cache が無効になります。
Router Cache を無効にする方法は?
完全無効化は難しいですが、Next.js 15 では `next.config.js` の `staleTimes` 設定でキャッシュ時間を 0 に設定することで実質無効化できます。または、必要なタイミングでクライアントコンポーネントから `router.refresh()` を呼び出して強制更新します。

8 min read · 公開日: 2025年12月19日 · 更新日: 2026年1月22日

コメント

GitHubアカウントでログインしてコメントできます

関連記事