Guide 12 · Developer docs

Performance, caching, deployment and go-live

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

The caching tiers

Six places keep a copy of something between requests, from compiled templates to the CDN and the visitor's browser. What each one holds, for how long and what clears it, so you can tell where a stale value came from.

Who this is for. The developer or site operator looking at a page that is stale, or slower than it should be. Afterwards you can name the copy a value came from, how long that copy lives and what clears it.

Core dna keeps six kinds of copy. Only two hold anything a visitor sees, and only one, the page cache, holds a whole page. The table is the short answer; the sections below say how each behaves.

TierHoldsLifetimeCleared by
Compiled templatesYour Smarty and Twig templates turned into PHPSmarty: until the file changes. Twig on production: until clearedA deploy, the environment's Clear Cache
In-request cacheFramework lookups made while one request runsOne requestThe end of the request
Shared MemcachedFramework internals: entity metadata, parsed queries, a few module lookupsUntil evictedA platform upgrade starts it empty
Page cacheWhole rendered pages, as files, per hostnameDefault cache expiration setting for pages, one day unless changedSmart invalidation on save, a deploy, Clear Cache, expiry
Resized imagesEvery resized or converted image, as filesDefault cache expiration setting for images, six weeks unless changedA new file uploaded to the image
CDN and browserPages (when CDN caching for pages is on), images and theme filesThe max-age the platform sent: a year for theme filesPurges sent with each of the above; nothing reaches the browser

Compiled templates

On an environment in production mode, each template is compiled to PHP the first time it is used, and the compiled copy is reused from then on. The two engines differ in one way that matters:

  • Smarty checks the template file's modification time and compiles it again when the file has changed.
  • Twig does not check on production. Its compiled copy is kept until the environment's compiled templates are deleted, which a deploy and the environment's Clear Cache both do.

On a development environment both engines compile every template on every request, which is one reason a page there takes several times longer to render than on production (development is not a benchmark).

A Twig template that did not arrive by deploy

On a Twig site in production mode, a template file that reached the server any other way than a deploy is not served until the environment's cache is cleared: the old compiled copy keeps answering. A Smarty site picks the change up on the next render. Either way, a change outside git is lost on the next deploy (FTP and tracked files).

The in-request cache

A plain PHP array that lives for one request. The framework keeps its own lookups in it, such as configuration and entity metadata, so asking twice in one render costs one look-up. Templates cannot use it, and it is gone when the response is sent. Nothing you see as stale comes from here.

Shared Memcached

On an environment in production mode, a Memcached layer sits behind the in-request cache and survives between requests. It holds framework internals: how each entity maps to the database, the parsed form of the platform's own queries, annotations and a handful of module lookups. It does not hold your records or your rendered output. Every render reads its content from the database.

A request skips Memcached, and works from the in-request cache alone, when:

  • The environment runs in development mode.
  • The request carries a PHPSESSID cookie. The platform starts a PHP session when it needs server-side state for a visitor: for a signed-in member on every render (the session holds the CSRF token), on the sign-in and registration pages, when a signed-out visitor is sent from a members-only page to sign in, and during external sign-in and passkey flows. A member's sign-in itself is a different cookie, token.
  • The request carries an Authorization header, as an API call does.

Its keys are namespaced by the framework version, the database and the site folder, so a platform upgrade starts it empty. There is no setting for it and nothing for you to clear. Authenticated requests and the cache follows the consequences.

The page cache

The tier that matters. A GET that renders a 200 for a visitor who is not signed in is written to a file, together with a short list of its response headers. The next request for the same URL is answered from that file before the platform has started the application, looked at cookies or connected to the database. That is why a cached page answers in tens of milliseconds at the server and a rendered one in hundreds.

  • Lifetime: Cache settings › Default cache expiration setting for pages, one day unless changed, with choices from one minute to twelve weeks. A template can set its own with setCacheTTL() or setCacheExpiryDate() (the other controls).
  • Key: the hostname and the path, plus the query parameters the template declared, by reading them through $_request->getParameter() or with $_response->addCacheVaryParameter(). Other query parameters, cookies and headers are not part of it (query parameters).
  • Per hostname: copies are stored under the hostname that was asked for, so www.example.com and example.com keep separate copies, and so does each environment.
  • Not on development: a development environment never stores a page.

What is and is not stored is on What makes a response uncacheable; how a stored page is thrown away is on Cache invalidation.

Resized images

Each size and format of an image that a page asks for is generated once and kept as a file for Default cache expiration setting for images, six weeks unless changed. The files are kept once for the whole account, shared by every site and environment, and this cache works on development environments too. The first request for a new size waits for it to be generated: on a production site in October 2026, first requests for new sizes of a 960 by 640 pixel image took 1.1 to 1.4 seconds to start answering, and 4.2 seconds for a 1600-pixel version. Every later request is a file. Uploading a new file to the image clears its sizes. Images covers asking for the right size.

The CDN and the browser

On an environment in production mode, images and theme files are always cacheable on the CDN. Pages are only when Cache settings › Enable CDN caching for pages is on. The platform tells the CDN and the browser what they may keep through Cache-Control:

  • A stored page, CDN caching on: public, max-age=<seconds left>, with a CDN-Tag naming the environment so one purge clears every page of it.
  • A stored page, CDN caching off: private, max-age=<seconds left>. No shared cache keeps it, but the visitor's own browser may, for the rest of the page's lifetime.
  • A page that is not stored: no-store.

The browser is the one copy nothing on the server can clear. A visitor who loaded a page an hour ago can be shown their own copy until it expires, whatever was purged since. For a page that changes by the minute, give that template a short lifetime rather than relying on invalidation.

Theme files

Files served from the site folder, such as the built CSS and JavaScript, fonts and theme images, are sent with Cache-Control: public, max-age=31536000 on production: browsers and the CDN may keep them for a year. A deploy purges the CDN's copies, but a browser keeps its own until the URL changes. That is why asset URLs carry a version, such as ?v={$_currentSite->getLastVcsHash()}, which changes with every deploy (seeing which commit is live).

Go deeper

Was this page helpful?

Your answer goes to the docs team.