HTTP Caching
Dune ships with three complementary caching mechanisms:
| Mechanism | Where it lives | What it does |
|---|---|---|
| ETag / 304 | HTTP headers | Lets browsers and CDNs validate cached copies without a full re-download |
| Cache-Control + SWR | HTTP headers | Tells CDNs how long to cache and how to serve stale content while revalidating |
| In-process page cache | Server memory | Avoids re-rendering popular pages on every request |
All three are enabled by default with conservative settings and work without any configuration changes.
ETag and 304 Not Modified
Dune automatically adds an ETag header to every rendered content page. The ETag is derived from the page's route, title, date, template, and format — no file I/O required.
On subsequent requests that include If-None-Match: "<etag>", Dune returns 304 Not Modified with an empty body. Browsers and CDNs use this to confirm their cached copy is still valid without transferring the full HTML again.
This works automatically with no configuration.
Cache-Control headers
Defaults
Rendered HTML pages receive:
Cache-Control: public, max-age=0, stale-while-revalidate=60
This means:
- Browsers must revalidate every time (via ETag/304 — almost always instant).
- CDNs and shared caches may serve a stale copy for up to 60 seconds while fetching a fresh one in the background.
Static assets (fonts, images, CSS, JS) keep their existing long-lived headers (max-age=31536000).
Configuring the default
# site.yaml
http_cache:
default_max_age: 300 # 5 min browser cache
default_swr: 3600 # 1 h CDN stale-while-revalidate
Per-route rules
Override the default for specific URL prefixes. The longest matching prefix wins; exact matches take priority over prefixes.
# site.yaml
http_cache:
default_max_age: 0
default_swr: 60
rules:
# Long-lived blog posts
- pattern: "/blog"
max_age: 3600
stale_while_revalidate: 86400
# Never cache the search page
- pattern: "/search"
no_store: true
# Docs can be cached aggressively
- pattern: "/docs"
max_age: 86400
stale_while_revalidate: 604800
| Field | Type | Description |
|---|---|---|
pattern |
string |
URL prefix or exact path |
max_age |
number |
Browser max-age in seconds |
stale_while_revalidate |
number |
CDN/shared-cache SWR in seconds |
no_store |
boolean |
Emit Cache-Control: no-store (disables all caching) |
In-process page cache
For high-traffic deployments, Dune can cache rendered HTML in server memory so popular pages don't get re-rendered on every request.
# system.yaml
page_cache:
enabled: true
max_entries: 500 # max pages held in memory
ttl: 30 # seconds before an entry is considered stale
warm: true # pre-load all pages at startup
How it works
- On the first request to a page, Dune renders the HTML and stores it with its ETag.
- Subsequent requests within
ttlseconds return the cached HTML directly — no template rendering. - After
ttlseconds the entry expires; the next request re-renders and refreshes the cache. - If the page's ETag is still current, a
304is returned instead of the full HTML regardless of the cache.
Cache warming
Set warm: true to pre-resolve all published pages in the background immediately after server startup. This avoids cold-start latency on the first request to each page after a restart.
Warming is fire-and-forget — the server starts accepting requests immediately; warming happens concurrently.
Tuning
| Setting | Default | Notes |
|---|---|---|
max_entries |
500 |
Each entry holds a rendered HTML string. A typical page is 10–50 KB; 500 entries ≈ 5–25 MB. |
ttl |
30 |
Shorter = fresher content, more CPU. Longer = faster responses, potentially stale for longer after edits. |
Monitoring
The /health endpoint includes cache statistics when the page cache is enabled:
{
"status": "ok",
"uptime": 3600,
"pages": 124,
"cache": {
"entries": 87,
"hits": 14203,
"misses": 211,
"hitRate": 0.985,
"evictions": 0
}
}
Recommended configurations
Small to medium sites (default)
No configuration needed. ETag/304 validation and a 60-second SWR window work well out of the box.
High-traffic server deployment
# system.yaml
page_cache:
enabled: true
max_entries: 1000
ttl: 60
warm: true
# site.yaml
http_cache:
default_max_age: 0
default_swr: 300
CDN-fronted deployment (Cloudflare, Fastly, CloudFront)
# site.yaml
http_cache:
default_max_age: 0 # browser always revalidates via ETag
default_swr: 3600 # CDN serves stale for 1 h while revalidating
rules:
- pattern: "/blog"
max_age: 0
stale_while_revalidate: 86400
- pattern: "/docs"
max_age: 3600
stale_while_revalidate: 604800
- pattern: "/api"
no_store: true
Static site
For dune build --static output, caching is handled entirely by the static host. HTTP caching config has no effect in static builds.