The Laioutr frontend is a Nuxt 3 app with SSR enabled by default and no HTTP caching configured out of the box. The server renders HTML for every request, hydrates on the client, and you opt in to CDN caching by adding route rules.
Data passes through three caching layers before reaching the user:
| Layer | Where | What it caches | Configured via |
|---|---|---|---|
| Orchestr Entity Cache | Nitro server (in-process) | Query, link, and component resolver results at the entity level | Per-handler strategy, ttl, buildCacheKey (see Orchestr Caching) |
| HTTP / CDN Cache | CDN edge or reverse proxy | Full HTML responses and API JSON responses | Cache-Control headers via routeRules or middleware |
| LRU Client Store | Browser (Pinia-backed) | Entity component data already loaded during the session | Automatic; no configuration needed |
useFetch / useAsyncData caching.When you call a server action via useFetchAction, the action runs once on the server during SSR and the result is transferred to the client via the serialized payload. The action does not re-execute on client hydration.
To have a CDN cache SSR responses, send Cache-Control headers via route rules. A typical starting point:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Default: short CDN TTL for HTML, no browser cache
'/**': {
headers: { 'Cache-Control': 'public, max-age=0, s-maxage=15, must-revalidate' },
},
// Don't cache session-specific pages
'/checkout/**': { headers: { 'Cache-Control': 'private, no-store, no-cache' } },
'/cart': { headers: { 'Cache-Control': 'private, no-store, no-cache' } },
'/login': { headers: { 'Cache-Control': 'private, no-store, no-cache' } },
},
});
nitro.routeRules if you prefer to keep Nitro config separate. The behaviour is identical.TTL guidance:
API routes (/api/orchestr/... or your own server routes) can also be cached by the CDN. Add routeRules for those paths with appropriate Cache-Control and Vary headers. Do not cache mutation endpoints or routes that return user-specific data.
In your module's setup, extend nuxt.options.routeRules to add cache rules for routes your module registers. Merge with existing rules rather than overwriting the whole object so other modules' rules are preserved.
If your frontend serves multiple markets, languages, or domains, the CDN must store separate cached entries for each variant. Without this, a German user could receive HTML cached from a French request.
Vary headersAdd a Vary header so the CDN keys on the relevant request headers:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/**': {
headers: {
'Cache-Control': 'public, max-age=0, s-maxage=30, must-revalidate',
'Vary': 'Accept-Language, X-Market',
},
},
},
});
If your setup passes market or currency information through custom headers (e.g. X-Market, X-Currency), include those in Vary. Be aware that each unique combination of varied headers creates a separate cache entry, so avoid varying on high-cardinality headers.
When locales are part of the URL path (e.g. /de/products/..., /fr/products/...), the CDN naturally caches them as separate entries since the URL differs. You can still define per-locale route rules:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
'/de/**': {
headers: { 'Cache-Control': 'public, max-age=0, s-maxage=60, must-revalidate' },
},
'/fr/**': {
headers: { 'Cache-Control': 'public, max-age=0, s-maxage=60, must-revalidate' },
},
},
});
When each market is served from a different domain (e.g. shop.de, shop.fr), most CDNs key on the Host header automatically. Verify this with your CDN provider. If you use a single CDN distribution across domains, add Host to your Vary header or configure CDN-level cache policies per origin domain.
For CDN caching to work, the SSR HTML for a given URL (and locale/market) must be the same for every user. Move personalized or session-specific content to the client:
<ClientOnly> or guard it with import.meta.client / onMounted.The third caching layer lives in the browser. When Orchestr returns entity data during a page load, resolved entity components are stored in a Pinia-backed LRU cache.
Consider a typical shopping flow. You browse a product listing page where each product card loads entity components (title, image, price). When you click through to a product detail page, the LRU store already has those components cached. They render immediately while the PDP fetches remaining components (description, stock, reviews).
Key characteristics:
product-title, product-image) across listing and detail pages to maximize cache hits.| What | Where | Default |
|---|---|---|
| SSR | nuxt.config.ts → ssr | true (Nuxt default) |
| HTML caching | routeRules → headers['Cache-Control'] | Not set |
| API response caching | routeRules for /api/** or per-handler headers | Not set |
| Multi-market cache keys | Vary header or URL-based locale prefixes | Not set |
| Orchestr data cache | Per-handler strategy, ttl, buildCacheKey | See Orchestr Caching |
| Client store | Automatic LRU | Always on |
Routing
How Laioutr generates routes from page types, how URL patterns are configured, and how markets affect route resolution.
Tracking
Laioutr’s tracking abstraction gives you a single API to send analytics and marketing events. Use one or more tracking adapters (e.g. Google Tag Manager), optionally gated by consent, and implement your own adapters for other tools.