Mobile Header CTA

D8 Performance: Caching

Thanks to its newly improved Cache API, Drupal 8 is faster than ever. Drupal 8 performance configuration is available on /admin/config/development/performance.

Now enabled by default, a new core module called Internal Page Cache caches pages for anonymous users. Pages requested by anonymous users are stored and reused, speeding up performance.

The only changeable setting in the Internal Page Cache is the "Page cache maximum age”, which sets the max-age value in the Cache-Control headers output by Drupal 8. Set the max-age as high as the frequency with which you are updating the pages. If your site will be getting a lot of traffic, set the max-age to a higher value. The higher the max-age, the better the information that is being cached and reused. Your CDN and frontend caching, such as Varnish, will utilize the max-age and serve the cached copy until it expires. To disable caching for development purposes, set the "Page cache maximum age" to no caching. The only value that will disable Drupal's caching is "no caching".

This module assumes pages are identical for all anonymous users. So if your website is serving personalized content, you will want to disable the Internal Page Cache module. Otherwise, Javascript and Ajax can handle the personalization on the front-end.

Alternatively, you can use Dynamic Page Cache. Dynamic Page Cache caches the pages minus the personalized sections. Hence, this is useful for both anonymous and authenticated users. Dynamic Page Cache uses the cache contexts, a declarative way to create context variations of all the items to be cached. These dynamic sections are marked with placeholders by Drupal 8 Render API, called auto-placeholdering. A placeholder is assigned and Drupal will postpone the rendering of the placeholder render array until the very last moment. The overall page with auto-placeholdering is cached by Dynamic Page Cache and Drupal continues to render cache fragments for those dynamic parts.

Cache max-age

Debugging cache

You can debug cacheable responses by setting the http.response.debug_cacheability_headers parameter to true in your services.yml. For this to take effect, the container must be rebuilt. That will cause Drupal to send the corresponding X-Drupal-Cache-Tags and X-Drupal-Cache-Contexts headers to cache tags and cache contexts.

Using cache contexts and cache tags

Use \Drupal\node\Entity\Node;

/**

* Implements hook_preprocess_block().

*/

function mymodule_preprocess_block(&$variables) {

if ($variables['elements']['#id'] == 'search_promo') {

// Unique cache per search query string.

$variables['#cache']['contexts'] = ['url.query_args:search'];

// Depends on content from a node entity.

$node = Node::load($variables['promo_nid']);

$variables['#cache']['tags'] = [$node->getCacheTags(];

}

}

Let’s say that we have a promo content block which will display different content based on the searches performed. The “search” query string will determine the cache context of the block. The block will be cached vary on the “search” query string, hence the url.query_args:search. The cache tags indicate that the cached block should be invalidated when the node entity content, where the block is loading, is updated.

Generally cache tags take the form of <entity type ID>:<entity ID> or config:<configuration name>, you should not rely on these directly. You should retrieve cache tags to invalidate for a single entity using its ::getCacheTags() method, e.g. $node->getCacheTags(), $user->getCacheTags(), $view->getCacheTags() etc.

To invalidate a cache use Drupal\Core\Cache\Cache;

Cache::invalidateTags($node->getCacheTags());

What are the available cache contexts

A quick trick to find the list of cache contexts you could use, search the files *.services.yml for cache_context.*.

Gotchas

The {{ content }} variable in template

You must render the {{ content }} variable to ensure that its cache tags bubble up and end up in the page cache. You may end up with an uncacheable page if the {{ content }} is not rendered.

So ensure your template contains:

{{ content }}

Or if you would like to render some fields separately

{{ content|without('field_coauthor', 'field_more_link') }}

Avoid random sort
A random sort in views or code will cause the page to be not cacheable.

All access-checking logic must now return AccessResultInterface objects, allows for cacheability metadata. If you implement any form of access or alter existing access hooks, ensure you return one of these:

return AccessResult::allowed();

return AccessResult::forbidden();

return AccessResult::neutral(); // The default if all conditions failed.