How Core dna caches pages, what makes a render fast, how code reaches a site and what stays behind, the go-live checklist, and running a site once it is live. For developers, leads and site operators who already build pages in a theme repository.
L2–L3
Unit 01 · The caching model
preventCaching() and fragments
The caching modelPage 4 of 34Updated
The deliberate tool for a response that differs for every visitor, where to put it so it costs one small render instead of a whole page, when not to use it, and the other cache controls on $_response.
Who this is for. Theme developers adding anything personal to a cached site. Afterwards you can keep a per-visitor part out of the cache without giving up the cache for the page around it, and set a page's lifetime from its template.
preventCaching() tells the platform not to store this response. It is the right call for output that genuinely differs per visitor, and the wrong one for a whole public page with one personal corner: there it turns a page served from a file into a full render for every visitor. The pattern that avoids the trade is a fragment, and the call belongs in the fragment's template.
What it does
Template code
{$_response->preventCaching()}
{% do _response.preventCaching() %}
The render is not stored, and the response carries Cache-Control: no-store and X-Cache-Status: miss, no-store, so neither a CDN nor the browser keeps it.
It can go anywhere in the render. The decision is taken after the templates have run, so a call inside an include or a component counts. That is also why one call in a shared partial switches the cache off for every page that includes it.
It does not remove a copy already stored. The page cache is read before your template runs, so a copy stored before you added the call is served until it expires or is cleared. A deploy clears it; a template that reached the server any other way needs the environment's Clear Cache.
It affects this response only. Nothing is remembered about the URL; the next render decides again.
In a fragment template
A fragment is a small template that renders only the personal part of a page: the member's links, a cart count, "your courses". The public page is cached as a shell, and a script fetches the fragment after the page has painted. The fragment is the only thing rendered per visitor, so it prevents caching and keeps its work small:
Three rules make a fragment work. The full pattern, with the layout that serves it and the script that fetches it, is written up once in Hybrid builds (its section on dynamic fragments), and from the member's side in a cached shell with member fragments:
Serve it from a URL that is never stored for a member, such as a page under /my-account, or one whose template prevents caching as above, so the fragment itself is never cached either.
Keep it small. It renders on every page view of every visitor, so a fragment that loads a menu or a product list costs more than the page cache saves.
Render a sensible default into the shell, such as a "Sign in" link, so the page works before the script runs and without it.
On a whole public page for one personal element. Fragment the element instead.
To make an edit show up sooner. Saving a record already clears the pages that showed it (cache invalidation). If a page goes stale, find out why rather than switching its cache off.
On a page that varies by a query parameter. Declare the parameter instead, and each value gets its own stored copy (query parameters). A search page works this way too.
The other controls
Call
Effect
Use it for
setCacheTTL(seconds)
This response's lifetime: for the stored copy and for the max-age sent to the browser and the CDN
A page that changes on a known rhythm: a short lifetime for a live scoreboard, a long one for an archive
Declares query parameters as part of the cache key without reading them
A parameter that a script or the controller uses and the template itself never reads
enforceCaching()
Stores the response even if the template prevented caching, a visitor is signed in, the status is not 200 or the path is excluded. Routes that prevent caching themselves, a visitor's language cookie and development environments still win
Almost never. A signed-in render stored this way is served to everyone
The lifetime belongs to the stored copy, not to the content: a page that expires at midnight is still cleared earlier if a record on it is saved. Set it in the template of the page that needs it, not in the base template, or every page inherits it.
Template code
{* An events page that shows "today": expire it when today ends *}
{$_response->setCacheExpiryDateFromString('tomorrow 00:00')}
{# An events page that shows "today": expire it when today ends #}
{% do _response.setCacheExpiryDateFromString('tomorrow 00:00') %}
"Tomorrow" is worked out in the site's time zone: the timezone setting in the global settings, which is UTC unless someone changed it. Where that is not the visitors' zone, name theirs in the string: 'tomorrow 00:00 Australia/Sydney' is the next midnight in Sydney, whatever the site's setting.