今更整理するNext.jsの「revalidatePath」「revalidateTag」「updateTag」

今更整理するNext.jsの「revalidatePath」「revalidateTag」「updateTag」
個人ブログ キャッシュ Cache-Control Next.js ISR

このページは、Next.jsのrevalidateTagとrevalidatePath、updateTagに関する整理メモである。

前提として押さえておきたい情報

そもそもrevalidateは、Webに昔からある概念である。 詳細を深掘りすると、Cache-Control ヘッダーキャッシュの議論にも行き着く。

(HTTP)キャッシュ

リクエストとレスポンスを保存する仕組み。過去のやりとりを保存しておくことで、再度同じ条件のリクエストをした時に、保存したレスポンスの内容を再利用(または再検証)して、表示の高速化や通信量削減・サーバ負荷軽減などを行う仕組み。

具体的には、

  • リクエスト情報をキーにし、対応するレスポンスを保存する
  • ブラウザキャッシュであれば、ユーザーのPC内 > 各ブラウザ指定のディスクなどに保存される
  • キャッシュにもいくつか種類があり、それぞれ仕様が異なる(共有/プライベートなど)
  • そのレスポンスがキャッシュ可能かどうかも関係ある
  • 後述するCache-Controlなども関係する

Cache-Control ヘッダー

ブラウザーや共有キャッシュ(プロキシーや CDN など)において、HTTPキャッシュを制御する仕組み。

Cache-control: max-age=180, publicのように、Cache-Control: <ディレクティブ>, <ディレクティブ>, ...のような形で書かれる。

リクエスト側・レスポンス側それぞれで使えるディレクティブが決まっている。

no-storeでキャッシュ保存しないようにしたり、max-agemust-revalidateなどで時間(キャッシュの鮮度)的に検証したり、文字通りキャッシュをコントロールするための指示を司っている。


上記に示した通り、このCache-Controlで使用できるディレクティブの中には、*-revalidate のようなものがある。ここでは詳細には書かないが、これらはいわゆる再検証を意味する。must-revalidateであれば、max-ageとともに併用され、キャッシュの時間的鮮度に応じて再検証を必須化する(再検証しないと再利用不可)。stale-while-revalidateであれば、キャッシュが古くなって再検証が必要な場合でも、設定した期間分は古いキャッシュを再利用できる。

このように、revalidateは再検証および古いキャッシュの扱い(再利用・破棄)に関連する概念である。

Next.jsのISR

ISRとは、Next.jsが提唱したIncremental Static Regenerationの略である。ここでは端的に、SSGやSSRのデメリットを解決できる可能性がある仕組みとして扱う。

たとえばSSGでは、基本的にプロジェクト全体を再ビルドすることでHTMLファイルなどを生成する。WebhookによるSSGワークフローであれば、Webhook受信後にプロジェクト全体を再ビルドし、CDNなどへキャッシュされた成果物を配信することで、ブラウザ描画に必要なファイルを届ける。

しかし、プロジェクトの規模が肥大化するほど、プロジェクト全体の再ビルドには時間がかかり、サーバー負荷も高まる可能性がある。

SSRは、パーソナライズ画面やリアルタイム描画が必要な場合に用いられる。これも非常に便利な仕組みだが、リクエスト数によってはサーバー負荷が膨大になる。

その上で、既存のレンダリング(SSG/SSRなど)のデメリットを、場合によっては代替できる仕組みとしてISRがある。基本的な仕組みは、キャッシュ + CDN + トリガー(Webhookや時間など)で構成されているものであり、トリガーを起点に、キャッシュうまく保存・再利用・再検証することで、CDNからいつでも素早くユーザーにコンテンツをお届けすると言うもの。

感じ方には個人差があるが、どちらかというと多少の遅延が許される静的寄りのプロジェクトを、効率よく運用する仕組みとしてISRは非常に便利だと感じる(よくWebhookを用いてOn-demand ISRを実装する)。

そしてISRには、大きく分けて以下の2つがある。

  • Time-based revalidation
  • On-demand revalidation

ざっくり説明すると、Time-basedは時間経過をトリガーに再検証を促すISRであり、On-demandは何かしらのイベント(たとえばCMSからのWebhook通知)をトリガーに再検証を促すISRである。

今回整理する revalidateTagrevalidatePath は、後者のOn-demand revalidation実装時に使用できるNext.js APIである。

revalidatePath

revalidatePathは、ルートごとにキャッシュを無効化する仕組みである。

Server Action1およびRoute Handler2で呼び出せる(基本的にサーバーで処理されるため、クライアントでは使えない)。

Pathの名の通り、以下のようなものを無効化できる。

  • ページ(指定ページのキャッシュ)
  • レイアウト(レイアウト配下のネストされたレイアウト/ページ)
  • Route Handler2(Handler内で参照されたデータなど)

基本的な構文は以下の通り。

import { revalidatePath } from 'next/cache';

revalidatePath(path: string, type?: 'page' | 'layout'): void;

revalidatePath() のシグネチャは以下の通り。

path には、具体のURL(例: /posts/1)やルートパターン(例: /posts/[id])を指定する。

type には page または layout を指定する。動的セグメント(xxx/[slug]など)を含むパターン指定時は必須であり、それ以外ではオプションである。

個人的には、以下のようにRoute Handler2でCMSなどのWebhookをトリガーとして、ページや特定URLのキャッシュ無効化・再検証に使うことが多い。 以下のような場合、指定したパスは次回訪問時にキャッシュ再検証される。

import { revalidatePath } from 'next/cache'

revalidatePath('/blog/post-1')

revalidateTag

revalidateTagは、キャッシュタグが付与されたコンテンツのキャッシュを無効化する仕組みである。

Server Action1およびRoute Handler2でのみ呼び出せる点は、revalidatePathと同様である(基本的にサーバーで処理されるため、クライアントでは使えない)。

Tagという名前の通り、以下のようにタグ付けされているモノのキャッシュを無効化できる。

  • fetch(..., { next: { tags: [...] } }) でタグ付けしたData Cache
  • 'use cache' + cacheTag(...) でタグ付けしたキャッシュ

基本的な構文は以下の通り。

import { revalidateTag } from 'next/cache';

revalidateTag(tag: string, profile: string | { expire?: number }): void;

revalidateTag() のシグネチャは以下の通り。

tag には対象タグを指定する。

profile には、max(stale-while-revalidate)またはカスタムのcacheLifeプロファイル名、即時失効のための { expire: 0 } などを指定できる。

実運用では、データ取得側でTagを付与し、更新イベント側(Webhook受信用Route Handler2など)で revalidateTag を呼ぶ構成にすることが多い。

const posts = await fetch('https://api.example.com/posts', {
  next: { tags: ['posts'] },
}).then((res) => res.json());
import { revalidateTag } from 'next/cache';
import { NextResponse } from 'next/server';

export async function POST(request: Request) {
  const body = await request.json();

  if (body.secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ ok: false }, { status: 401 });
  }

  revalidateTag('posts', 'max');
  return NextResponse.json({ ok: true });
}

このようにしておくと、CMS更新をトリガーに posts タグ付きキャッシュを再検証対象にできる。profile: 'max' を使えば stale-while-revalidate で更新されるため、即時全体再計算を避けつつ整合性を保ちやすい。

updateTag

revalidatePath・revalidateTag以外にも、updateTag というAPIも存在する。

updateTag は、タグ付きキャッシュを即時失効させるAPIである。revalidateTag('tag', 'max') と異なり stale を返さず、次の読み取りでfreshデータ取得を待つのが特徴である。
公式ブログのNext.js v16 リリース(2025年10月21日公開)で updateTag()(new)として追加が明記されており、利用前提はNext.js v16以降である。

基本的な構文は以下の通り。

import { updateTag } from 'next/cache';

updateTag(tag: string): void;

updateTag()Server Actions1でのみ呼び出せる(Route Handler2内で使用しようとするとエラーになる)。
これにより、「更新した本人には直後に必ず新しいデータを見せたい」というread-your-own-writesの要件を満たしやすくなる。
read-your-own-writes とは、自分が書き込んだ更新結果を直後の読み取りで必ず自分が観測できる一貫性の考え方である。たとえば投稿作成直後に一覧へ戻った時、作成した投稿がすぐ表示される状態を担保したい場合に重要になる。

具体例としては、投稿作成や編集のServer Action1でDB更新直後にupdateTagを呼ぶケースが分かりやすい。

'use server';

import { updateTag } from 'next/cache';
import { redirect } from 'next/navigation';
import { db } from '@/lib/db';

export async function createPostAction(formData: FormData) {
  const title = String(formData.get('title') ?? '');
  const post = await db.post.create({ data: { title } });

  updateTag('posts');
  updateTag(`post-${post.id}`);

  redirect(`/posts/${post.id}`);
}

このようにしておくと、更新直後の一覧・詳細画面で古いキャッシュを返しにくくなる。

3つそれぞれの比較

API一言概要無効化スコープ対象キャッシュ呼び出せる場所次回表示時の挙動向いているユースケース
revalidatePathパス単位で再検証指定ページ/レイアウト/Route Handler2パス主にData Cache + その再レンダリング結果Server Functions3 / Route Handlers2Pathに対して次回訪問で再検証(Server Function時は表示中UIへ即時反映あり)「このページ群だけ」更新したい時
revalidateTagタグ単位で再検証同じタグを使う全ページ横断タグ付きData CacheServer Functions3 / Route Handlers2profile: 'max' なら stale-while-revalidate共有データを複数画面で使う時、Webhook連携
updateTagタグ単位で即時失効同じタグを使う全ページ横断タグ付きData CacheServer Actions1のみ次リクエストはfresh取得を待つ(staleを返さない)read-your-own-writesを厳密に担保したい時

revalidatePathとrevalidateTagの違い

  • 粒度が違う
    • revalidatePath: ルート(URL/ルートパターン)起点
    • revalidateTag: データタグ起点
  • 影響範囲が違う
    • revalidatePath: 指定したパスに紐づく表示更新が中心
    • revalidateTag: 同タグを使う別ページにも横断的に影響
  • 設計の使い分け
    • ページ/レイアウト単位で明確に更新したいなら revalidatePath
    • データソース単位で複数画面をまとめて整合させたいなら revalidateTag

revalidateTagとupdateTagの違い

  • 更新ポリシーが違う
    • revalidateTag(tag, 'max'): stale-while-revalidate(ユーザーに古い内容を返しつつ裏で更新)
    • updateTag(tag): 即時失効(次リクエストはfresh取得を待つ)
  • 呼び出し可能コンテキストが違う
    • revalidateTag: Server Functions3 / Route Handlers2
    • updateTag: Server Actions1のみ
  • 使い分けの基準
    • 多少の遅延許容・負荷平準化重視なら revalidateTag('tag', 'max')
    • 「更新した本人には直後に必ず新しい結果を見せる」なら updateTag

参考資料

Footnotes

  1. Server Actionとは: https://nextjs.org/docs/app/getting-started/updating-data

  2. Route Handlerとは: https://nextjs.org/docs/app/getting-started/route-handlers

  3. Server Functionsとは('use server' で定義し、サーバー実行関数を使用できる。内部の実態は、Reactの機能): https://nextjs.org/docs/app/api-reference/directives/use-server#server-functions

記事を共有する