Drupal 8 arrived with several performance improvements — and performance means caching.
In this article we will look in detail at the cache types provided by Drupal, and how to use them in our custom modules to ensure optimal performance for users.
Before diving into the varieties of cache backends, I want to mention that Drupal 8 provides several modules installed by default:
Below is a demonstration of how BigPipe works:

Drupal's cache API uses several bins tied to cache tables in the database (prefixed with cache_). You must first request a specific cache bin to interact with the cache API.
$render_cache = \Drupal::cache('render');The $render_cache variable above represents a render cache bin object. Note that in this example the cache is called statically. When working within classes — as is standard in custom Drupal modules — it is recommended to use dependency injection by injecting the cache service (cache.render in our example).
The cache API (backend) is based on three fundamental principles that must be well understood to use effectively in custom module development:
Cache tags work on a simple principle: they allow you to tag cached content and render elements with specific tags, which are later invalidated after a defined action.
Example: Imagine a simple Article content type displayed on its own detail page, listing page, or other pages. If we don't manage the cache in the render array that displays content, items will be cached and we may end up showing stale content to users.
We can use the node_list tag on all render arrays that display content, ensuring that whenever content is modified, all render arrays tagged with node_list are invalidated and rebuilt with fresh content.
<entity type ID>:<entity ID> or config:<configuration name>. If the object only accepts one instance, use the object name as the tag.node_list, config:system.performance).Cache tags in Drupal 8 do not support tags by bundle. This issue will be resolved in future versions. In the meantime, the Handy Cache Tags module handles this limitation efficiently, allowing bundle-level tags (e.g., handy_cache_tags:node:article).
As the name implies, a cache context is based on contexts defined by Drupal — in other words, cache contexts allow you to vary cache in a render array by path, user, language, theme, or other contexts.
Cache Max-Age lets you cache a render array for a specific duration. It takes a positive integer representing seconds (0, 60, 100, etc.).
Example usage of cache in render arrays:
//Case of render array
$build['#cache']['max-age'] = 0;
//Case of plugin bloc (OOP way)
public function getCacheMaxAge()
{
return 0;
}As mentioned at the beginning of this article, Drupal has the Internal Dynamic Page Cache (manages cache for authenticated users) and Internal Page Cache (manages cache for anonymous users). The latter does not recognize cache max-age — meaning that if you add a max-age in a render array, it will still be cached indefinitely for anonymous users due to Internal Page Cache ignoring max-age.
It is therefore not recommended to use cache max-age in custom development. It is not used in Drupal Core source code either.