対象:Next.js(App Router中心)でWebシステムを作る人。初心者〜中級者向けに、紛らわしいポイントは図解・補足つきで丁寧に解説します。
0. この記事でわかること
- Next.jsの4つのキャッシュ(Request Memoization / Data Cache / Full Route Cache / Router Cache)の役割と違い
-
よく使うAPI:
fetch
のcache
/next.revalidate
/next.tags
、revalidatePath
/revalidateTag
、dynamic
、draftMode
、新機能「use cache
指令」などの使いどころ -
やりがちミス(
cookies()
やheaders()
で静的化が崩れる、開発時にデータが更新されない 等)の回避 - 運用の型:時間ベース再検証、オンデマンド無効化、HTTPヘッダー、画像・静的アセット、自己ホスト/複数コンテナでのキャッシュ永続化
用語ミニ解説
- 静的生成(SSG/ISR):事前にHTMLやRSCペイロードを作ってキャッシュから返す方式
- 動的レンダリング(SSR):リクエスト毎にサーバでレンダリング
- 再検証(Revalidate):キャッシュを時間や明示命令で更新すること
1. 全体像:Next.jsのキャッシュは4層で考える
仕組み | 何をキャッシュ | どこに | 目的 | 期間/Scope | 主なAPI/設定 |
---|---|---|---|---|---|
Request Memoization | 同一リクエスト内の同一 fetch 結果 |
サーバのメモリ(1リクエスト内) | 同じデータの二重取得を防止 | リクエスト中のみ | 自動(fetch 同一URL+同一options時) (Next.js) |
Data Cache |
fetch のレスポンス
|
サーバ側(永続も可) | リクエスト跨ぎでデータ再利用 | 永続/再検証可能 |
fetch(..., { cache, next:{ revalidate, tags }}) / revalidateTag / revalidatePath / use cache (Next.js) |
Full Route Cache | HTML と RSC ペイロード | サーバ | レンダリングコスト削減 | 永続/再検証可能 | ルートが静的なときに有効。dynamic , revalidate で制御 (Next.js) |
Router Cache | RSCペイロード(クライアント遷移用) | ブラウザ | 画面遷移を高速化 | セッション/時間ベース |
staleTimes (実験的), Linkのprefetch 。Next.js 15ではPageは既定0秒 (Next.js) |
2. まず覚える実務ショートレシピ
2-1. 時間ベース再検証(ISRの感覚で)
// app/products/page.tsx
export default async function Page() {
const res = await fetch('https://api.example.com/products', {
next: { revalidate: 3600 }, // 1時間ごとに再検証
})
const data = await res.json()
return <Products data={data} />
}
- 効果:同じデータは Data Cache から返り、期限切れ後の最初のアクセスで自動再生成。 (Next.js)
2-2. オンデマンド無効化(更新後に特定ページ/一覧を即更新)
// app/actions.ts
'use server'
import { revalidatePath, revalidateTag } from 'next/cache'
export async function createItem() {
// DB insert...
revalidatePath('/items') // 一覧を更新
revalidateTag('item-42') // タグ紐づけfetchを更新
}
-
使い分け:画面単位は
revalidatePath
、データ単位はrevalidateTag
が便利。 (Next.js)
2-3. 強制的に毎回最新(個別の箇所だけ)
import { unstable_noStore as noStore } from 'next/cache';
export default async function Page() {
noStore(); // このコンポーネント配下をキャッシュ対象外に
const res = await fetch('https://api.example.com/me', { cache: 'no-store' })
// ...
}
-
ポイント:
noStore()
は細粒度で便利(dynamic = 'force-dynamic'
より限定的に使える)。 (Next.js)
2-4. 「use cache
指令」でルート全体の再検証ポリシーを宣言(Next.js 15)
// app/blog/[slug]/page.tsx
'use cache' // ルートのキャッシュ/再検証ポリシーを宣言的に
import { cacheLife, cacheTag } from 'next/cache'
cacheLife('hours') // 推奨される再検証の目安(例)
cacheTag('blog') // データにタグ付け→ revalidateTag('blog') で更新
- 効果:ルート単位で「どれくらい持たせるか/何で無効化するか」を宣言的に定義できる。
3. fetch
のキャッシュ挙動(Next.js 15の要点)
- 既定の
options.cache
は"auto no cache"
(ビルド時は静的化、動的要素があれば都度取得という“賢い既定”)
明示したい場合はcache: 'force-cache' | 'no-store'
。再検証はnext: { revalidate: 秒 }
を使用。 -
next.tags: string[]
でタグを付けると、revalidateTag('tag')
で関連データだけを無効化できる。 (Next.js) - **開発時(HMR)**は
fetch
結果がHMR間で再利用されるため、即時に最新に見えないことがある(ナビゲーション/リロードでクリア可。serverComponentsHmrCache:false
で無効化可)。 (Next.js)
補足:Request Memoization(重複排除)
同一リクエスト内で同じfetch(URL, options)
を複数回呼んでも1回だけ実行される。レイアウト/ページ/子コンポーネントのあちこちで同じ取得を書けるのはこのため。ただしスコープはその1リクエスト中だけ。 (Next.js)
4. 静的/動的レンダリングとキャッシュの関係
-
静的にできるなら静的化(Full Route Cache有効)
revalidate
を与えればISR、tags
を付ければ部分的にオンデマンド更新可。 (Next.js) -
動的化のトリガ
cookies()
/headers()
/ リクエスト依存のAPI などリクエスト時情報を使うと動的レンダになり、Full Route Cacheはスキップされる。
どうしても静的化したい場合は、その利用を避ける/分離する。 (Next.js) -
ルート設定での強制
export const dynamic = 'force-dynamic'
(常に動的)やexport const revalidate = 0
はFull Route Cache + Data Cacheを両方スキップ。部分的な非キャッシュはnoStore()
を優先。 (tigerabrodi.blog)
5. ルーターキャッシュ(クライアント側)
- Next.js 15ではPageセグメントは既定で staleTime=0(=遷移時は常に最新を取得)。必要に応じて実験的
staleTimes
で調整可能。
例)next.config.ts
でexperimental.staleTimes
を設定。 (Next.js) -
Link
のprefetch
は先読みしてRouter Cacheに格納、体感速度を上げる。詳細は公式のPrefetchガイドへ。
6. Route Handler(API)・Pages Routerのメモ
-
Route Handler (app/*/route.ts)**
Next.js 15ではGET
も既定でキャッシュされない。必要ならexport const dynamic = 'force-static'
等で静的化。HTTPヘッダー(Cache-Control
)でもCDNキャッシュを制御可能。 (Next.js) -
Pages Router(
pages/
)-
getStaticProps
+revalidate
でISR。 -
getServerSideProps
は毎回実行だが、レスポンスヘッダーでCDNキャッシュ(s-maxage
/stale-while-revalidate
等)が可能。 (Next.js)
-
7. HTTPキャッシュ(CDN/ブラウザ)と画像・静的ファイル
7-1. HTTPヘッダー
// app/api/items/route.ts
export async function GET() {
const data = await getItems()
return Response.json(data, {
headers: {
'Cache-Control': 'public, s-maxage=600, stale-while-revalidate=60',
},
})
}
-
s-maxage はCDN向け、stale-while-revalidateで高速応答 + 背景更新が可能(CDN対応必要)。Vercelでは
CDN-Cache-Control
やVercel-CDN-Cache-Control
の優先順位も利用可。 (Vercel)
7-2. 画像(next/image
)
-
最適化画像は強いキャッシュ制御がかかる。Static Image Import(
import img from './a.png'
)はハッシュ付きで実質永続キャッシュに向く。minimumCacheTTL
調整も可能。 (Reddit)
7-3. public/
フォルダの静的アセット
- 既定は**
Cache-Control: public, max-age=0
(内容が変わる可能性があるため)。長期キャッシュさせたいものはファイル名にハッシュ**を付ける or 画像はStatic Import経由で。 (Next.js)
8. よくある混乱ポイントと対処
-
「開発中、データが更新されない」
→ HMR間でfetch
結果が保持されます。ナビゲーション/リロードでクリア。serverComponentsHmrCache:false
で無効化可。 (Next.js) -
「
cookies()
を使ったら静的化できなくなった」
→ それは動的化シグナル。対象コンポーネントを分離し、静的部分と動的部分を切り分ける。 (Next.js) -
「GET Route Handlerがキャッシュされない」(Next 15)
→ 既定が非キャッシュ化。静的応答にしたいときはdynamic = 'force-static'
やヘッダーで明示。 (Next.js) -
「どこまでがData Cacheで、どこからがFull Route Cache?」
→ データはfetch
側(revalidate/tags
)で管理、HTML/RSCはルートの静的/動的で決まる、で切り分けると理解しやすい。 (Next.js)
9. 運用の型(実例セット)
9-1. 一覧×詳細のよくある構成
- 一覧:
next: { revalidate: 300, tags:['items'] }
- 詳細:
next: { revalidate: 3600, tags:[
item-${id}] }
- 更新アクション後:
revalidateTag('items')
とrevalidateTag('item-123')
を両方呼ぶ
→ 一覧と該当詳細だけ即時更新され、他ページはキャッシュ維持。 (Next.js)
9-2. 管理画面など常に最新が必要
- ページ冒頭で
noStore()
を呼ぶ /fetch(...,{ cache:'no-store' })
- ただし全体no-storeはコスト大。必要な一部だけをno-storeにするのがコツ。 (Next.js)
9-3. 公開ブログ・商品カタログ
- 記事/商品ページ:
revalidate: 3600
- トップ/カテゴリ:
revalidate: 300
+tags:['top','category:abc']
- CMS更新Webhook → API Routeで
revalidateTag('top')
/ 該当カテゴリやスラッグのタグをまとめて無効化。 (Next.js)
10. 自己ホストや複数コンテナ環境でキャッシュを共有/永続化したい
-
next.config.js
のcacheHandler
に独自実装(例:Redis)を指定すると、Data Cache/Full Route Cacheを外部ストアに保存できる。大規模/オートスケール構成で有効。// next.config.js module.exports = { cacheHandler: require.resolve('./cache-handler.js'), cacheMaxMemorySize: 0, // プロセス内メモリを使わない }
カスタムハンドラは
get/set/revalidateTag/resetRequestCache
を実装。 (Next.js)
11. テスト&デバッグ
-
本番挙動の確認:
next build && next start
でISR/再検証の挙動をローカル再現。
キャッシュデバッグ:.env
にNEXT_PRIVATE_DEBUG_CACHE=1
を入れてログ確認。 (Next.js) -
fetchログ:
next.config.ts
のlogging.fetches.fullUrl = true
で開発時のfetch
を可視化。 (Stack Overflow)
12. 画像・静的アセットの実践Tips
- 頻繁に変わらないロゴ等はStatic Image Importで永続キャッシュ。ユーザー生成画像は
minimumCacheTTL
を短めに。 (Reddit) -
public/
は既定でローカルキャッシュ0。長期キャッシュさせたい場合はファイル名にハッシュを入れる運用に。 (Next.js)
13. 迷ったらここだけチェック(チェックリスト)
-
データは基本:
revalidate
ortags
。常時最新が必要な所だけnoStore()
-
画面を静的化できるか?(
cookies()
/headers()
をむやみに使っていないか) -
ルート全体の方針は
'use cache'
+cacheLife
/cacheTag
で宣言(Next 15) - APIや画像はHTTPヘッダーでCDNキャッシュを設計
- 開発時に「更新されない」はHMRキャッシュの仕様を理解(ナビゲーション/リロード or 無効化)
-
複数コンテナ/オートスケールなら
cacheHandler
(外部ストア)を検討
付録A:コピペスニペット集
A-1. fetch
:時間ベース + タグ
await fetch('https://api.example.com/posts', {
next: { revalidate: 600, tags: ['posts'] },
})
A-2. Server Action からのオンデマンド再検証
'use server'
import { revalidatePath, revalidateTag } from 'next/cache'
export async function publishPost(slug: string) {
// await db.publish(slug)
revalidatePath(`/blog/${slug}`, 'page')
revalidateTag('posts')
}
A-3. ルート強制動的(避けたいが、どうしても…の時に)
// app/dashboard/page.tsx
export const dynamic = 'force-dynamic'
A-4. Route Handler でCDNキャッシュ
export async function GET() {
return Response.json({ ok: true }, {
headers: { 'CDN-Cache-Control': 'max-age=300, stale-while-revalidate=60' },
})
}
A-5. use cache
(Next 15)
'use cache'
import { cacheLife, cacheTag } from 'next/cache'
cacheLife('minutes') // or seconds/hours/days
cacheTag('invoices')
参考(公式ドキュメントの該当箇所)
-
fetch
の既定・next.revalidate
・HMRキャッシュ等:Next.js 15.5.3 APIリファレンス(fetch
) (Next.js) - Caching全体像(4層の整理・用語):Caching in Next.js ガイド (Next.js)
-
use cache
/cacheLife
/cacheTag
:Directives: use cache(Next.js 15) - ルーターキャッシュと
staleTimes
、Next.js 15の既定変更:Next.js 15ブログ/設定リファレンス (Next.js) - Route Handlers のキャッシュ方針(Next 15):Next.js 15 リリースノート (Next.js)
- ISR/再検証の検証方法(
NEXT_PRIVATE_DEBUG_CACHE=1
):ISRガイド (Next.js) -
noStore()
の推奨(粒度の細かい非キャッシュ):unstable_noStore
リファレンス (Next.js) -
public/
フォルダの既定キャッシュ:Public Folder リファレンス (Next.js) - 画像キャッシュの考え方(Static Image Import / TTL):
next/image
リファレンスの該当点 (Reddit) - 自前キャッシュハンドラ(Redis等)で永続化:
cacheHandler
リファレンス (Next.js)
まとめ
-
原則:まずは
fetch
のrevalidate/tags
でData Cacheを設計 → ルートは可能な限り静的化(Full Route Cache) -
更新:ユーザー操作系は Server Action →
revalidatePath
/revalidateTag
で即座に最新化 -
例外部位は
noStore()
で最小範囲に限定 - 運用:HMRキャッシュ・Router Cacheの特性(Next 15の既定変更)を理解し、開発/本番の差を把握
このままQiitaに貼り付けてOKです。必要なら、あなたのプロジェクト構成(API階層/DB/デプロイ先)に合わせた具体的な設計テンプレも作ります!