0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Next.jsで“ちゃんと動く”キャッシュ戦略——基礎から実装・運用まで

Last updated at Posted at 2025-09-16

対象:Next.js(App Router中心)でWebシステムを作る人。初心者〜中級者向けに、紛らわしいポイントは図解・補足つきで丁寧に解説します。


0. この記事でわかること

  • Next.jsの4つのキャッシュ(Request Memoization / Data Cache / Full Route Cache / Router Cache)の役割と違い
  • よく使うAPIfetchcache / next.revalidate / next.tagsrevalidatePath / revalidateTagdynamicdraftMode新機能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 = 0Full Route Cache + Data Cacheを両方スキップ。部分的な非キャッシュは noStore() を優先。 (tigerabrodi.blog)

5. ルーターキャッシュ(クライアント側)

  • Next.js 15ではPageセグメントは既定で staleTime=0(=遷移時は常に最新を取得)。必要に応じて実験的 staleTimes で調整可能。
    例)next.config.tsexperimental.staleTimes を設定。 (Next.js)
  • Linkprefetch先読みして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-ControlVercel-CDN-Cache-Controlの優先順位も利用可。 (Vercel)

7-2. 画像(next/image

  • 最適化画像は強いキャッシュ制御がかかる。Static Image Importimport img from './a.png')はハッシュ付きで実質永続キャッシュに向く。minimumCacheTTL 調整も可能。 (Reddit)

7-3. public/ フォルダの静的アセット

  • 既定は**Cache-Control: public, max-age=0(内容が変わる可能性があるため)。長期キャッシュさせたいものはファイル名にハッシュ**を付ける or 画像はStatic Import経由で。 (Next.js)

8. よくある混乱ポイントと対処

  1. 「開発中、データが更新されない」
    → HMR間でfetch結果が保持されます。ナビゲーション/リロードでクリア。serverComponentsHmrCache:false で無効化可。 (Next.js)

  2. cookies() を使ったら静的化できなくなった」
    → それは動的化シグナル。対象コンポーネントを分離し、静的部分と動的部分を切り分ける。 (Next.js)

  3. 「GET Route Handlerがキャッシュされない」(Next 15)
    → 既定が非キャッシュ化。静的応答にしたいときはdynamic = 'force-static'やヘッダーで明示。 (Next.js)

  4. 「どこまでが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.jscacheHandler に独自実装(例: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/再検証の挙動をローカル再現。
    キャッシュデバッグ:.envNEXT_PRIVATE_DEBUG_CACHE=1 を入れてログ確認。 (Next.js)
  • fetchログnext.config.tslogging.fetches.fullUrl = true で開発時のfetchを可視化。 (Stack Overflow)

12. 画像・静的アセットの実践Tips

  • 頻繁に変わらないロゴ等はStatic Image Import永続キャッシュ。ユーザー生成画像はminimumCacheTTLを短めに。 (Reddit)
  • public/既定でローカルキャッシュ0。長期キャッシュさせたい場合はファイル名にハッシュを入れる運用に。 (Next.js)

13. 迷ったらここだけチェック(チェックリスト)

  • データは基本:revalidate or tags。常時最新が必要な所だけ 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)

まとめ

  • 原則:まずは fetchrevalidate/tagsData Cacheを設計 → ルートは可能な限り静的化(Full Route Cache)
  • 更新:ユーザー操作系は Server Action → revalidatePath/revalidateTag即座に最新化
  • 例外部位noStore()最小範囲に限定
  • 運用:HMRキャッシュ・Router Cacheの特性(Next 15の既定変更)を理解し、開発/本番の差を把握

このままQiitaに貼り付けてOKです。必要なら、あなたのプロジェクト構成(API階層/DB/デプロイ先)に合わせた具体的な設計テンプレも作ります!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?