Skip to content
This page View Markdown Open in ChatGPT Open in Claude

Caching

A website is fast before any cache : its JSX is compiled to string templates when a file is saved, each thread of the server keeps an engine per website with its code loaded, and a page reads its data next to the database. A page of a hundred rows renders in a tenth of a millisecond of JavaScript.

On top of that, the server keeps whole pages.

Pages kept by the server

export const config: PageConfig = {
    cache: {
        maxAge: 60,                 // kept 60 seconds
        staleWhileRevalidate: 300,  // then sent for 300 more while one request makes it anew
        scope: 'public',            // one page for every anonymous visitor
        tags: ['products'],         // dropped by ctx.revalidate('products')
        query: ['page', 'sort'],    // the parts of the query which make another page
    },
};
scope Kept for A signed-in visitor
'public' Every anonymous visitor : one page Gets a page of its own, never one kept for others
'user' Each visitor : one page per visitor Gets its own kept page
'none' (default) Nobody
  • cache of a layout applies to the pages inside it ; cache of site.config.ts to every page which says nothing.
  • A page is kept under its website, the version of the website, its path, the parts of its query (query, all of them by default, in their order), and the visitor for 'user'. A save of the website drops every kept page : a new version is a new key.
  • The answer says what happened in x-am-cache : MISS (made and kept), HIT (sent as kept), BYPASS (not kept, see below).
  • The preview of the panel never keeps pages.
  • What is kept has a size in all (ssr.cacheMb of the server, 64 MiB by default) : past it, the pages used longest ago go. A page larger than a quarter of it is not kept.

Dropping kept pages

pages/api/products.ts
export async function POST(ctx: PageContext) {
    // ... a product is saved ...
    await ctx.revalidate('products');
    return { saved: true };
}

ctx.revalidate(...tags) drops the pages of these tags in this server. A pull of git and a save in the panel drop every page of the website on every server.

What is never kept for everyone

A page kept for everyone shows nothing of its visitor. The server keeps none which :

  • was made for a request with messages of ctx.flash ;
  • set cookies, or keeps a message for the next page ;
  • read what differs from one visitor to the next : ctx.cookies, ctx.headers, ctx.ip, ctx.locale or ctx.csrf. Such a page is sent (x-am-cache: BYPASS), not kept ;
  • did not answer 200.

What a loader writes into a page is up to it : never put a value of ctx.user into a page with scope: 'public' — a signed-in visitor never gets a public page, but keep the rule anyway.

A count in the header without losing the cache

The shop template shows the count of the cart in its header with a script, from a cookie which holds no secret : its pages stay the same for every anonymous visitor, and the server keeps them.

What browsers keep

  • A page kept for everyone is sent with cache-control: no-cache : browsers ask again (the page holds the CSRF token of its visitor), the server answers from its cache.
  • A page per visitor : private, max-age=…. A page of a signed-in visitor otherwise : private, no-store.
  • A JSON endpoint with scope: 'public' which read nothing of its visitor : public, max-age=…, stale-while-revalidate=… — a CDN may keep it.
  • Public files with the hash of their content (asset('/app.css')) : immutable, a year. Without it : an ETag, and 304 when nothing changed.

The settings of the server

Setting Default What it is
ssr.threads the cores Threads which render pages
ssr.enginesPerThread 64 Websites a thread keeps loaded
ssr.memoryMb 64 Memory of an engine
ssr.cpuMs 2000 Time of the code of one render
ssr.timeoutMs 30000 Time of one render in all, the waits for data included
ssr.cacheMb 64 Size of the pages kept, 0 for none

See API Maker configuration.