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

This is a Drupal 8 specific related guide

See this for Drupal 9

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);
  }

}

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;

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

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

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

      // Only cache by Cookies on Node routes.
      $node = $request->attributes->get('node');
      if ($node instanceof NodeInterface) {
        $cid_parts[] = $request->cookies->get('my_cookie_name', 'default');
      } 

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

    return $this->cid;
  }

}

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

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

Important

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.

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