Skip to main content
Version: main (5.3)

Caching

Since 6.0

Routes which return static, public assets (such as language strings and templates) can be made cacheable by browsers and reverse proxies by setting the cachelifetime property of the \core\router\route attribute.

Declaring a cacheable route​

cachelifetime accepts any relative time string understood by DateInterval::createFromDateString(), for example '90 days'.

A cacheable route
#[\core\router\route(
path: '/strings/{language}/{component}[/{identifier:.*}]',
method: ['GET'],
security: [],
cachelifetime: '90 days',
pathtypes: [
new \core\router\parameters\path_language(),
new \core\router\parameters\path_component(),
],
)]
danger

Responses are cached as public and immutable. Only use cachelifetime for routes whose response is identical for every user and which never contains private data.

The cache key​

When cachelifetime is set, the route automatically gains an optional cachekey path parameter (\core\router\parameters\path_cachekey), which is a Unix timestamp placed at the start of the path:

/api/rest/v2/strings/en/core # No cache key - not cached
/api/rest/v2/cachekey:1737584047/strings/en/core # Cacheable

Because the cache key forms part of the URI, changing it (typically by using the current JavaScript/theme revision) invalidates every cached copy of the resource. You do not need to declare the parameter yourself.

Cache headers​

The \core\router\middleware\cache_control_middleware adds the following headers when all of these are true:

  • the request method is GET;
  • the response status is 2xx;
  • the route has a cachelifetime;
  • a numeric cachekey was supplied, and it is not in the future (to prevent cache poisoning); and
  • the lifetime resolves to a positive number of seconds.
HeaderValue
Cache-Controlpublic, max-age=<seconds>, immutable
ExpiresThe current time plus the lifetime
Pragma(empty)
Accept-Rangesnone

The expiry is calculated from the current time, not from the cache key, so cached content does not expire early because an old key was used.

If any condition is not met, no caching headers are added.

Fetching cacheable routes from JavaScript​

Pass the cachekey option to the core/fetch module:

import Fetch from 'core/fetch';

const response = await Fetch.performGet('core', `strings/en/core`, {cachekey: M.cfg.jsrev});

Requests with a cache key bypass the batcher by default, so that every URL can be cached individually.