ISR revalidation windows
Four cached scopes on four windows, over upstreams that change at different rates: crypto prices every few seconds, station measurements every fifteen minutes, reference rates once a weekday, and repository counters whenever the repo changes.
Watch each panel's age, which ticks live in your browser. When it passes the profile's revalidate value, the entry is eligible for regeneration.
What to check
The two-reload rule. Let the market panel's age pass 15s. Reload once and the stamp does not move, because you got the stale entry while regeneration ran in the background. Reload again and it moves.
Cached against live. The two market panels read the same endpoint in the same request. Their prices drift apart within the window and re-converge when the cached one regenerates.
Two clocks. The station panel has a 60s cache window but its observed time only advances every 15 minutes, because that is how often the upstream publishes. A fresh cache entry does not mean new data. The FX panel shows the same thing with a 5 minute window over data that changes once a weekday.
From the shell. for i in $(seq 1 6); do curl -s localhost:3000/isr | grep -m1 -o 'data-exec="[^"]*"'; sleep 5; done. The id repeats, then changes, on that two-request cadence.
Fast window, 15 seconds
cacheLife('isr-15'): stale 30s, revalidate 15s, expire 10m. The left panel is cached; the right one is the same fetch with no cache at all, rendered in the same request.Market — cached 15s
use cache- cacheLife
- isr-15
- cacheTag
- market
- upstream
- 85ms
ran at exec m2fgrjage …
- bitcoin$84,571 · published 21:29:50Z
- ethereum$2,693.33 · published 21:29:50Z
Tagged 'market', so the buttons below can expire it without waiting for the window.
Market — live
suspense fallbackstreaming at request time. This is what ships in the static shell.
Medium and slow windows
Station — 60s window
use cache- cacheLife
- isr-60
- cacheTag
- stations, stations:london
- upstream
- 490ms
ran at exec 6hsmo1age …
station London
- temperature
- 16.3°C
- humidity
- 63%
- wind
- 10.1km/h
- pressure
- 1026.4hPa
- elevation
- 16m
- observed
- 2026-10-01T21:30Z
The upstream publishes every 15 minutes, so the observed time changes far less often than this cache refills.
FX rates — 5m window
use cache- cacheLife
- blog
- cacheTag
- fx
- upstream
- 331ms
ran at exec wc1ukbage …
1 USD · published 2026-10-01
- CHF0.83528
- EUR0.88511
- GBP0.75565
- JPY157.98
The ECB publishes reference rates once per weekday, so a 5 minute window over them is mostly wasted work. Match the window to how often the upstream changes.
Repo counters — 1h window
use cache- cacheLife
- hours
- cacheTag
- repo
- upstream
- 236ms
ran at exec c78u8oage …
- stars
- 142,988
- forks
- 33,559
- open issues
- 3,545
- pushed
- 2026-10-01T21:23:37Z
A rate-limited upstream needs a long window. A 403 here means the hourly quota is used up. It resets on its own and nothing else depends on it.
Skip the wait
Invalidate an ISR window early
revalidateTag('market', 'max'): Stale-while-revalidate. The next reload still shows the OLD stamp; the one after shows the new one.revalidateTag('market', { expire: 0 }): No stale window. The next reload blocks on a fresh fetch and the stamp moves.revalidateTag('stations:london', { expire: 0 }): The same for the station panel. On the Pantheon handler an immediate invalidation re-runs every cached scope on the route, so the other cached panels move too.