Confusion about revalidateTag's profile parameter: does it only use expire? Does it override cacheLife? #94014
Replies: 6 comments 5 replies
-
|
Hey @zhaozhiguang, Great question. Your testing correctly identified how Does
|
| Property | What It Does | When You See It |
|---|---|---|
stale |
How long the client caches without checking the server | After revalidateTag is called, old content is still served for this duration as fresh content loads in the background |
revalidate |
How often the server refreshes the cache | The system periodically revalidates the cache; stale content may be served during this window while revalidating |
expire |
Maximum time cache stays alive before blocking for fresh content | This was the visible threshold in your test—the point where it finally returned fresh content |
Your test showed the expire threshold clearly because you waited until expire was reached, causing a blocking request for fresh content.
Does revalidateTag Override cacheLife?
Yes, for invalidation purposes, but the original cacheLife profile stays attached to the cached entry.
Think of it this way:
cacheLifesets the original policy for the cached data when it was storedrevalidateTag('tag', 'minutes')says: "For this invalidation event, behave like the 'minutes' profile"
The cached entry doesn't rewrite its stored lifetime policy. Instead, revalidateTag can override how this specific invalidation acts — particularly the stale window during revalidation.
From the Next.js documentation: the second argument "allows you to specify any cache life profile that your application has defined, allowing for custom revalidation behaviors tailored to your specific caching requirements". This is per‑invalidation override behavior.
The 5-Minute "Stale" Window You Observed
Your observation about both "max" and "minutes" behaving the same for ~5 minutes is accurate and expected.
Looking at the default cache profiles:
| Profile | stale | revalidate | expire |
|---|---|---|---|
| seconds | 0 | 1s | 60s |
| minutes | 5m | 1m | 1h |
| hours | 5m | 1h | 1d |
| days | 5m | 1d | 1w |
| max | 5m | 30d | ~indefinite |
Why the 5-minute stall: The stale window is designed to prevent cache stampedes. Under heavy load, if every request hit an expired cache simultaneously and waited for fresh data, your origin server could be overwhelmed. Stale-while-revalidate gives you 5 minutes of serving old content while new content is fetched in the background.
The profile you specify changes what happens after that initial 5‑minute window:
"minutes": After stale period, revalidates every ~1 minute, expires in ~1 hour"max": After stale period, revalidates every ~30 days, expires in ~1 year
Practical Recommendation
For most use cases, use revalidateTag(tag, 'max'). It gives you the immediate stale-while‑revalidate behavior during the 5‑minute window, but won't block for background refreshes.
If you need immediate expiration (e.g., a webhook just updated critical data), you can force it with revalidateTag(tag, { expire: 0 }).
Beta Was this translation helpful? Give feedback.
-
|
Hey @zhaozhiguang, Yes, you've got it exactly right. The Simple Explanation
Your Understanding is Correct
✅ Correct. The cache naturally follows its profile regardless of manual invalidation.
✅ Correct. They reference the same set of profiles (
✅ Correct. That's the only difference — manual invalidation instead of waiting for time‑based triggers. The Missing Piece: Override Behavior
Example:
Bottom LineYour mental model is correct. |
Beta Was this translation helpful? Give feedback.
-
|
Hey @zhaozhiguang, You are right. Your test proves that the original What Actually HappensAfter Your test confirms this:
The original Corrected Understanding
Why This Makes SenseOnce you manually invalidate a cache tag and explicitly tell Next.js to use a different profile, the system assumes you want that new behavior going forward. There's no "revert" mechanism. Bottom LineYour test is correct. My mental model was wrong. |
Beta Was this translation helpful? Give feedback.
This comment was marked as low quality.
This comment was marked as low quality.
-
Calling The profile is used to collect its As the docs say, the normal flow here should be, you call revalidateTag, then the next request arrives N seconds later. If this is still within the
No, if this is happening, we should boil it down to a reproducible bug. Time based and on-demand revalidation, are just two different strategies to revalidate tagged data.
Interesting take, it does not, as said above the profile exists to mark the time after which, stale data won't be served, and rather block until revalidation is complete. This idea of passing the profile is relatively new, I hadn't considered that folks would think it overrides your code defined lifetime. That'd be one side-effect that'd totally make working with this API difficult.
How were these exactly? What did you do? How was it structured? Nested |
Beta Was this translation helpful? Give feedback.
-
|
Your testing is very thorough! Let me clarify how How
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Summary
I'm trying to understand the exact meaning of the profile parameter in revalidateTag(tag, profile), especially when using built-in profiles like "minutes", "days", or custom cache life profiles.
I ran a test comparing profile="max" and profile="minutes". Here's what I observed:
With both profiles, the first request after calling revalidateTag returns stale content (old data) for about 5 minutes.
After those 5 minutes, both profiles trigger an async background request to fetch fresh data, but the response still returns the stale content.
This continues until the expire time is reached.
For profile="minutes", expire is 1 hour (by default). After 1 hour, minutes finally returns fresh content.
I didn't test max fully, but I assume it would return fresh content after its expire (maybe 1 day or longer).
Based on this test, it seems that the profile parameter in revalidateTag only really uses the expire value from the profile, and the stale/revalidate settings don't seem to affect the observed behavior. Is that correct?
Also, a more fundamental question: Does revalidateTag's profile override the original cacheLife profile for the cached data?
For example, if I initially cache data with cacheLife('max'), then later call revalidateTag(tag, 'minutes'), will the data from that point on behave according to the "minutes" profile (i.e., shorter revalidate and expire), or does it keep the original "max" lifetime?
The current documentation is ambiguous on this point. It says profile "specifies the revalidation behavior" and "allows customizing revalidation behavior to your specific caching requirements", but it doesn't clearly state whether this replaces the lifetime policy of the cached data.
Could the docs be clarified on:
Whether revalidateTag's profile changes the effective stale/revalidate/expire of the cached data (overriding cacheLife), or only affects the revalidation strategy (e.g., stale-while-revalidate vs blocking)?
In practice, does the profile only use the expire value, or are stale and revalidate also relevant after a manual revalidation?
Thank you.
Additional information
Example
No response
Beta Was this translation helpful? Give feedback.
All reactions