Let FSR cache it for you
You'll make pages fast without writing a cache. FSR works out which routes read nothing of the request, renders those once and answers them from a file. For the ones that do read the request, it caches the expensive half and leaves the rest live. You don't tag anything, invalidate anything or reach for a key.
Before you start
Straight from crates.io. No Node, no package manager.
cargo install snapfire_compiler
cargo install snapfire_fsr_cli
fsr --version
Every command and screenshot on this page was captured with fsr 0.x.
Turn on the render memo
One section. Without it nothing is cached at all.
[cache]
capacity = 1000
ttl = "1m"That's the in-memory memo for rendered documents. It's the boring part.
Prerender the pages that never change
A route with no parameter, whose every loader is lowered and reads nothing of the request, renders the same for everybody. FSR already knows which those are. Ask it:
$ fsr dev myapp/app
prerender /about dist/prerender
/pricing dist/prerender
warm layout.promo not warmed
Now point the config at somewhere to put them and render them:
[server]
prerender = "dist/prerender"$ fsr prerender myapp/app
/about dist/prerender/about/index.html
/pricing dist/prerender/pricing/index.html
From then on a GET for those paths is answered from the file with an x-sf-prerendered: 1 header. Your session cookie and your middleware still run. One file is written per locale, so /about and fr_FR/about/index.html sit side by side and the request's locale picks.
What makes a route dynamic
Reading the request. A loader that touches a parameter, the query, the session or the clock keeps its route live. So does a Rust source, because the host can't see what a Rust function reads.
There is one useful exception. A route whose only request read is the identity gets prerendered for the anonymous public. The report says so:
prerender /posts dist/prerender for anonymous visitors
Signed-out visitors get the file. Signed-in ones are rendered live and memoized under their own subject. That's also how a preview works with no draft flag and no secret: the editor signs in, their loader's call carries their token, the public keeps the file.
Cache just the slow part
Here is the part that usually needs hand-tuning. One layout is enough to make a whole subtree dynamic. A console whose header shows who you're watching means every page under it reads the request, however fixed the page itself is. The route verdict is correct and too coarse to be useful, because the cost was never the render, it was the loader.
So FSR classifies per source as well as per route. fsr prerender runs every source under warm once with no request behind it, then writes the results beside the documents.
$ fsr prerender console/app
warm layout.promo 2 loads memoized
widths
At boot the host reads that file. A request reaching one of those sources takes the loaded value instead of running the loader. No service call, no interpreter. The console's header is still per session; the fixed layout above it costs nothing.
Why it can't go stale
A request never writes to the memo. Only a build fills it. A source the build did not reach costs a load every time, which is slow rather than wrong. Nothing grows without bound. A rebuild is what refreshes it.
That's the whole invalidation story. No key to build, no tag to attach, no revalidate to guess at.
No cache keys to maintain
No cache keys, no per-route TTL, no tag-based invalidation. No list of what is safe to cache, because the build works that out from what each loader reads.
Next up: 050. Find out what won't compile, which is how FSR tells you a loader just became dynamic.