GitHub

Caching

Pulse handles asset caching automatically — production bundles are content-hashed and served with immutable cache headers. Page caching is controlled declaratively in the spec: serverTtl for in-process data, cache for HTTP headers. Nothing is cached by default unless you declare it.

serverTtl — in-process data cache

serverTtl is a number of seconds to cache the result of server.data() in memory. Subsequent requests within the TTL window skip the async data fetch entirely and serve the cached result.

export default {
  route: '/homepage',
  serverTtl: 60,   // cache server data for 60 seconds
  server: {
    data: async () => ({
      featured: await db.products.getFeatured(),
      stats:    await analytics.getGlobalStats(),
    }),
  },
}
ValueBehaviour
undefined (default)No caching — server.data() runs on every request
0No caching (same as undefined)
60Cache for 60 seconds — at most one database hit per minute
3600Cache for 1 hour
The in-process cache is per-process and per-route. It is not shared across multiple server instances. Use a distributed cache (Redis, etc.) for cross-instance consistency.
In development, setting a long serverTtl can mask stale data. Set it to 0 or omit it during development, and add it when deploying to production.

cache — HTTP response headers

The cache field controls the Cache-Control header sent with the page HTML response. This tells browsers and CDNs how to cache the response.

export default {
  route: '/blog/:slug',
  cache: {
    public:               true,    // allow CDN/proxy caching
    maxAge:               300,     // cache for 5 minutes
    staleWhileRevalidate: 86400,   // serve stale for up to 24 hours while revalidating
  },
  // ...
}

Cache field reference

FieldTypeDescription
publicbooleanIf true, emits public — allows CDN/proxy caching. Default: private.
maxAgenumberSeconds before the response is considered stale. Emits max-age=N.
staleWhileRevalidatenumberSeconds to serve stale content while revalidating in the background. Emits stale-while-revalidate=N.
// private page — user-specific content
cache: { public: false, maxAge: 0 }
// → Cache-Control: private, no-store

// public marketing page — cached at CDN
cache: { public: true, maxAge: 3600, staleWhileRevalidate: 86400 }
// → Cache-Control: public, max-age=3600, stale-while-revalidate=86400

Default HTML caching

By default, Pulse sends Cache-Control: no-store for all HTML responses. Users always see fresh content — stale HTML is never served from browser or proxy caches unless you explicitly declare a cache policy in the spec.

Asset caching

Static assets in public/ receive Cache-Control: max-age=3600 (one hour).

Production bundles in public/dist/ receive Cache-Control: public, max-age=31536000, immutable (one year, immutable). This is guaranteed safe because bundle filenames include a content hash — code changes produce a new hash, and browsers fetch the updated file automatically. There is nothing to configure.

Development vs production

ResourceDevelopmentProduction
HTML pagesno-storeno-store (or your cache config)
Static assets (/public/*)max-age=3600max-age=3600
JS bundles (/dist/*)N/A (source files served directly)immutable, max-age=31536000
For maximum performance, combine serverTtl for expensive database queries with a short cache.maxAge and a generous cache.staleWhileRevalidate. Users get fast responses; data stays reasonably fresh.