👀

revalidateTag と updateTag は何が違う? Next.js 16 のソースコードを読んで理解する

に公開

はじめに

Commune Developers Advent Calendar 2025 シリーズ1 の9日目の記事です 🚀

Next.js 16 では、キャッシュの動作をより明示的に制御するための新しいAPI が導入されたり、既存のAPIが更新されたりと更なる進化を遂げましたね。そのなかでも revalidateTag と updateTag に私は関心がありました。公式の仕様や、以下の実際に動かしてみたブログを読んだりしてわかったような気になりました。

https://zenn.dev/usuijuice/articles/2ca2ed05a79f47

でも、なんとなくまだ自分のなかに落とし込めておらず、いくつか疑問がありました。

  • revalidateTag と updateTag はまったくの別物なのか、そうでないのか 💭
  • revalidateTag のプロファイルなしの動作は updateTag と書いてあるが本当なのか 💭
  • revalidateTag の max とか カスタムプロファイルってなにしてるのか 💭

などなど。以前に「Next.js revalidatePath/revalidateTagの仕組み」という記事を読んで、バチーンっと仕様が繋がり理解度がぐっとあがりました。ということで、今回は自分で Claude Code を駆使しながら実装を調査してみます。

https://zenn.dev/akfm/articles/nextjs-revalidate

v16 で revalidateTag は何が変わった?

あらためて revalidateTag の仕様変更を確認します。公式ドキュメントのタイトルのとおり、引数がまず変わりました。

https://nextjs.org/docs/messages/revalidate-tag-single-arg

そのため、revalidateTag は現在3つの使い方があります。

  • 第2引数なし。従来の使い方で非推奨である
  • 第2引数にプロファイル名を指定する。"max" が推奨されている
  • 第2引数に expire プロパティを持つ object を指定する
// Before (deprecated)
revalidateTag('posts')

// After
revalidateTag('posts', 'max')

revalidateTag('posts', { expire: 0 })

max プロファイルとは?

max とは何でしょうか? max は「Preset cache profiles」の一つです。ドキュメントをみると他にもプロファイルが定義されていることが読み取れますね。

https://nextjs.org/docs/app/api-reference/functions/cacheLife#preset-cache-profiles

これらってどこに定義されているのでしょうか?
それは config-shared.ts で defaultConfig として export されています。next.config.ts に自分で Custom cache profiles を定義することができますが、定義の仕方は Preset も同じようです。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/config-shared.ts#L1432-L1474

revalidateTag と updateTag は同じ?違う?

revalidateTag の仕様としてこのような記載があります。

Without profile: legacy behavior which is equivalent to updateTag
引用 Differences from revalidateTag - Functions: updateTag | Next.js

本当に同じでしょうか?コードを追ってみると定義は revalidateTag も updateTag と同じファイルでした。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/cache.js#L5-L11

そして、build 前の revalidateTag の定義はこれです。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/web/spec-extension/revalidate.ts#L24-L31

build 前の updateTag の定義はこれです。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/web/spec-extension/revalidate.ts#L39-L53

どちらも revalidate 関数を呼び出していることがわかります。その違いは第3引数に profileundefined を渡すかどうかですね。すでにまったくの別物ではなさそうな気がしてきました。

revalidate 関数をずーっと読んでいくと、最後に cacheLife の取得をしている処理がありました。profile が Object ならそのままつかって、string なら store から取得してます。{ expire?: number } を渡したときはそのまま使うんですね。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/web/spec-extension/revalidate.ts#L218-L230

if を読むと、profile がないときか expire が 0 のときに store.pathWasRevalidated = true してます。pathWasRevalidated ってなんでしょうか?ブログをお借りします。

サーバーからはページのRSC payloadが返され、それを契機にRouter Cacheもパージされます。
Server ActionsがページのRSC payloadを返すかは以下pathWasRevalidatedに基づいて判定されています。
引用 revalidateとRouter Cacheのクリア - Next.js revalidatePath/revalidateTagの仕組み

つまり、updateTag では profile が undefined なので、Router Cache がパージされることになりそうです。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/app-render/action-handler.ts#L1143-L1152

expire が 0 はどんなときでしょうか? 公式サイトに以下の記述がありました。

Good to know: For webhooks or third-party services that need immediate expiration, you can pass { expire: 0 } as the second argument: revalidateTag(tag, { expire: 0 }). This pattern is necessary when external systems call your Route Handlers and require data to expire immediately.
引用 route-handler - Functions: revalidateTag| Next.js

このパターンは、Route Handler で即時更新したいときに使うということみたいです。
これまでの調査結果から

  • revalidateTag と updateTag は内部的に同じ関数を呼んでる
  • revalidateTag のプロファイルなしの動作は updateTag と同じだった
  • revalidateTag の max とか カスタムプロファイルで Router Cache の revalidate の振る舞いを決めている

ということがわかりました(と思う)。めでたし、めでたし。

おまけ

ここから先はおまけです。せっかくなので周辺コードも読んでみました。

cacheLife の使われ方

revalidate 関数をみてると store.pendingRevalidatedTags に profile も push する処理が追加されてることがわかります。pendingRevalidatedTags とはなんでしょうか?

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/web/spec-extension/revalidate.ts#L207-L212

またブログをお借りします。

store.pendingRevalidates[tag]にincrementalCache.revalidateTag(tag)の処理自体を格納していることがわかります。このincrementalCache.revalidateTag(tag)こそが、revalidate情報を永続化する処理であると推測されます。
引用 revalidatePath/revalidateTagの定義から処理を追う - Next.js revalidatePath/revalidateTagの仕組み

つまりここの処理ですね。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/revalidation-utils.ts#L178-L180

その前の処理をみると、durations には cacheLife から expire を取り出して詰めていることがわかります。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/revalidation-utils.ts#L161-L165

cacheLife がどう使われるのかわかりました。

refresh() って何者?

Next.js に新しく追加されたAPIです。公式的にはこういう API です。

refresh allows you to refresh the client router from within a Server Action.
Functions: refresh | Next.js

コードを読んでみましょう。といってもとてもシンプルで、pathWasRevalidated = true しておわりです。つまり、Router Cache をパージしてくれるAPIですね。

https://github.com/vercel/next.js/blob/cf480fc1c110945c69fa0a33bc2ae4f356a8e01e/packages/next/src/server/web/spec-extension/revalidate.ts#L55-L79

おわりに

今回は Next.js のソースコードを追って revalidateTag と updateTag の違いを調査しました。説明的に似てるような、でも名前の違う両者のAPIの関係性をコードを読むことで理解することができました。公式ドキュメントの仕様もより腑に落ちた気がします。
Claude Code のおかげもあって、飽きずに疑問が浮かぶままに調査することができました。読みたいけどちょっと億劫という人も LLM を使って調査してみることをおすすめします👍

コミューン株式会社

Discussion