Page Cache by cookies(or anything available in the HTTP Request) - D9

This is a Drupal 9 specific related guide

See this for Drupal 8

Let's assume you have some sort of settings your users can choose and by visiting the same URL the user is presented with a personalized version of the page based on the chosen option.

The main trick here is to keep the Drupal's core Page Cache module enabled, to benefit from the powerful caching mechanism.

To achieve this, we're going to extend the core's Page Cache class by adding our cookie value variation to the Cache ID.

By default, the Cache ID is composed with complete page URL $request->getSchemeAndHttpHost() . $request->getRequestUri() and the Request format $request->getRequestFormat(NULL). All we need to do here is just to add the cookie value as part of this ID.

First, we need to alter the http_middleware.page_cache service by pointing to our custom implementation.

Assuming you have a module called cookies_page_cache, add a PHP file at cookies_page_cache/src/CookiesPageCacheServiceProvider.php and here we're going to point to our own class which we'll create later.


namespace Drupal\cookies_page_cache;

use Drupal\cookies_page_cache\StackMiddleware\CookiesPageCache;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\DependencyInjection\ServiceModifierInterface;

 * Overrides the Core PageCache service.
class CookiesPageCacheServiceProvider implements ServiceModifierInterface {

   * {@inheritdoc}
  public function alter(ContainerBuilder $container) {
    $container->getDefinition('http_middleware.page_cache')->setClass(CookiesPageCache::class)->addArgument(new Reference('http_middleware.kernel_pre_handle'));


Second, we need to create the Class mentioned previously and override the getCacheId() method by including our cookie value.

So, we need to place our class at cookies_page_cache/src/StackMiddleware/CookiesPageCache.php


namespace Drupal\cookies_page_cache\StackMiddleware;

use Drupal\page_cache\StackMiddleware\PageCache;
use Drupal\node\NodeInterface;
use Symfony\Component\HttpFoundation\Request;
use Drupal\Core\Routing\RouteMatch;
use Drupal\Core\StackMiddleware\KernelPreHandle;

 * Extends PageCache to include cookie value in the Cache ID.
class CookiesPageCache extends PageCache {

   * Kernel PreHandle.
   * @var \Drupal\Core\StackMiddleware\KernelPreHandle
  protected $kernelPreHandle;

   * {@inheritdoc}
  public function __construct(HttpKernelInterface $http_kernel, CacheBackendInterface $cache, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy, KernelPreHandle $kernelPreHandle) {
    parent::__construct($http_kernel, $cache, $request_policy, $response_policy);

    $this->kernelPreHandle = $kernelPreHandle;

   * @inheritdoc
  protected function getCacheId(Request $request) {

    if (!isset($this->cid)) {
      $cid_parts = [
        $request->getSchemeAndHttpHost() . $request->getRequestUri(),

      // Only cache by Cookies on Node routes.
      $node = $request->attributes->get('node');

      // Node can still be empty at this point. Try to load it manually with
      // the "current_route_match" service.
      if (empty($node)) {
        try {
          // Prepare everything so we can retrieve route parameter.
          $routeMatch = RouteMatch::createFromRequest($request);
          $node = $routeMatch->getRawParameter('node') ?? NULL;
        catch (\Exception $e) {
          // Catch exception, but don't do anything. Drupal will deal with that
          // later on.

      if ($node instanceof NodeInterface) {
        $cid_parts[] = $request->cookies->get('my_cookie_name', 'default');

      $this->cid = implode(':', $cid_parts);

    return $this->cid;


And the last thing is to add the cache context to your cache array

$build['#cache']['contexts' ][] = 'cookies:my_cookie_name';


One thing to keep in mind, as you're serving different page versions on the same URL the browser will cache the results of the page. I'm solving this by using Varnish so the response is adequate.

********************************** ************************* ************************ **************** ****************** *********** ************** ************* ************ *************