Cache Tag Reference
The {% cache %} tag stores the rendered HTML of a block so its body is skipped on subsequent requests. It is output (fragment) caching — different from Twig's template compilation cache, which only avoids re-parsing your .twig files but still re-runs them every request.
Use it for expensive fragments that change rarely: navigation menus, sidebars, related-post lists, {% cmsgrid %} listings, footers.
Syntax
{% cache key ttl=seconds tags=['collection'] shared=bool %}
{# expensive markup #}
{% endcache %}
Parameters
key(required) — a string expression identifying this fragment. Vary it by whatever the content depends on, e.g.'sidebar:' ~ page.id.ttl(optional) — lifetime in seconds. Defaults to thecache.fragmentTtlconfig value (3600).tags(optional) — a list of collection ids. When any object in a tagged collection is created, updated, or deleted, fragments carrying that tag are invalidated automatically — no manual cache clearing.shared(optional, defaultfalse) — see Logged-in users.
Examples
Simple, time-based
{% cache 'home-promo' ttl=900 %}
{{ cms.collection.objects('promos')|first.headline }}
{% endcache %}
Cached for 15 minutes, then re-rendered on the next request.
Auto-invalidating with a collection tag
{% cache 'latest-posts' tags=['blog'] %}
{% for post in cms.collection.objects('blog')|slice(0, 5) %}
<a href="{{ cms.url(post) }}">{{ post.title }}</a>
{% endfor %}
{% endcache %}
This fragment is served from cache until any blog object changes, at which point it is rebuilt on the next request. The ttl still applies as a backstop.
Per-page fragments
{% cache 'related:' ~ post.id tags=['blog'] %}
{# related posts for this specific post #}
{% endcache %}
Each post gets its own cached fragment because the key varies by post.id.
Logged-in users
By default, caching is bypassed for authenticated requests. A logged-in visitor always sees a freshly rendered fragment, and nothing personalized is ever stored under a shared key. This keeps access-group / member content from leaking between users.
If a fragment is identical for everyone (it does not depend on who is logged in), opt back into caching with shared=true:
{% cache 'global-footer' shared=true %}
{# identical for all visitors #}
{% endcache %}
Only use shared=true when you are certain the markup is the same for anonymous and logged-in visitors alike.
How invalidation works
Each tag has an internal version counter. A fragment's storage key embeds the current version of its tags, so when content in a tagged collection changes, the counter is bumped and every fragment keyed on the old version is instantly bypassed (the orphaned entries expire on their own TTL). This is automatic — you never call a clear function.
Notes
- Fragments are stored in the normal Total CMS cache (APCu → Redis → Memcached → filesystem) and are wiped by any global cache clear (devmode,
/emergency/cache/clear). - Caching is skipped automatically in devmode so you always see fresh output while editing.
- Set
cache.fragmentstofalsein config to disable the tag globally;cache.fragmentTtlsets the default lifetime. - If a cache backend errors, the block falls back to a live render — it never breaks the page.