 core/lib/Drupal/Core/Cache/CacheContexts.php       | 26 ++++++++++++++++++++++
 .../Cache/ConfigurableCacheContextInterface.php    | 26 ++++++++++++++++++++++
 .../lib/Drupal/Core/Cache/LanguageCacheContext.php | 10 ++++++++-
 .../Core/Cache/MenuActiveTrailCacheContext.php     | 11 ++++++++-
 core/lib/Drupal/Core/Cache/ThemeCacheContext.php   | 11 ++++++++-
 .../Core/Render/MainContent/HtmlRenderer.php       |  3 +++
 core/lib/Drupal/Core/Render/Renderer.php           |  7 ++++--
 .../user/src/Cache/UserRolesCacheContext.php       | 13 ++++++++++-
 8 files changed, 101 insertions(+), 6 deletions(-)

diff --git a/core/lib/Drupal/Core/Cache/CacheContexts.php b/core/lib/Drupal/Core/Cache/CacheContexts.php
index 33f86b1..19caae0 100644
--- a/core/lib/Drupal/Core/Cache/CacheContexts.php
+++ b/core/lib/Drupal/Core/Cache/CacheContexts.php
@@ -118,6 +118,32 @@ public function convertTokensToKeys(array $context_tokens) {
   }
 
   /**
+   * Gets cache tags associated with cache context tokens.
+   *
+   * @param string[] $context_tokens
+   *   An array of cache context tokens.
+   *
+   * @return string[]
+   *   The array of corresponding cache tags.
+   *
+   * @throws \InvalidArgumentException
+   */
+  public function getCacheTagsForTokens(array $context_tokens) {
+    $tags = [];
+    foreach (static::parseTokens($context_tokens) as $context) {
+      list($context_id, $parameter) = $context;
+      if (!in_array($context_id, $this->contexts)) {
+        throw new \InvalidArgumentException(String::format('"@context" is not a valid cache context ID.', ['@context' => $context_id]));
+      }
+      $service = $this->getService($context_id);
+      if ($service instanceof ConfigurableCacheContextInterface) {
+        $tags = Cache::mergeTags($service->getCacheTags($parameter));
+      }
+    }
+    return $tags;
+  }
+
+  /**
    * Retrieves a cache context service from the container.
    *
    * @param string $context_id
diff --git a/core/lib/Drupal/Core/Cache/ConfigurableCacheContextInterface.php b/core/lib/Drupal/Core/Cache/ConfigurableCacheContextInterface.php
new file mode 100644
index 0000000..d26b65e
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/ConfigurableCacheContextInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Cache\ConfigurableCacheContextInterface.
+ */
+
+namespace Drupal\Core\Cache;
+
+/**
+ * Provides an interface for defining a configurable cache context service.
+ */
+interface ConfigurableCacheContextInterface {
+
+  /**
+   * The cache tags associated with the cache context.
+   *
+   * @param string|null $parameter
+   *   The parameter, in case of a calculated cache context.
+   *
+   * @return string[]
+   *   The assocaited cache tags.
+   */
+  public function getCacheTags($parameter = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/Cache/LanguageCacheContext.php b/core/lib/Drupal/Core/Cache/LanguageCacheContext.php
index b9d19a7..1a78ef4 100644
--- a/core/lib/Drupal/Core/Cache/LanguageCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/LanguageCacheContext.php
@@ -12,7 +12,7 @@
 /**
  * Defines the LanguageCacheContext service, for "per language" caching.
  */
-class LanguageCacheContext implements CacheContextInterface {
+class LanguageCacheContext implements CacheContextInterface, ConfigurableCacheContextInterface {
 
   /**
    * The language manager.
@@ -54,4 +54,12 @@ public function getContext() {
     return implode(':', $context_parts);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    // @todo Update as part of https://www.drupal.org/node/2428837
+    return [];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Cache/MenuActiveTrailCacheContext.php b/core/lib/Drupal/Core/Cache/MenuActiveTrailCacheContext.php
index 7c41bee..e7c6f6f 100644
--- a/core/lib/Drupal/Core/Cache/MenuActiveTrailCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/MenuActiveTrailCacheContext.php
@@ -15,7 +15,7 @@
  * This class is container-aware to avoid initializing the 'menu.active_trail'
  * service (and its dependencies) when it is not necessary.
  */
-class MenuActiveTrailCacheContext extends ContainerAware implements CalculatedCacheContextInterface {
+class MenuActiveTrailCacheContext extends ContainerAware implements CalculatedCacheContextInterface, ConfigurableCacheContextInterface {
 
   /**
    * {@inheritdoc}
@@ -33,4 +33,13 @@ public function getContext($menu_name) {
     return 'menu_trail.' . $menu_name . '|' . implode('|', $active_trail);
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * @see \Drupal\Core\Config\Entity\ConfigEntityBase::getCacheTags()
+   */
+  public function getCacheTags($menu_name) {
+    return ['config:system.menu.' . $menu_name];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Cache/ThemeCacheContext.php b/core/lib/Drupal/Core/Cache/ThemeCacheContext.php
index ad0249a..ae3985c 100644
--- a/core/lib/Drupal/Core/Cache/ThemeCacheContext.php
+++ b/core/lib/Drupal/Core/Cache/ThemeCacheContext.php
@@ -13,7 +13,7 @@
 /**
  * Defines the ThemeCacheContext service, for "per theme" caching.
  */
-class ThemeCacheContext implements CacheContextInterface {
+class ThemeCacheContext implements CacheContextInterface, ConfigurableCacheContextInterface {
 
   /**
    * The theme manager.
@@ -46,4 +46,13 @@ public function getContext() {
     return $this->themeManager->getActiveTheme()->getName() ?: 'stark';
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * @see \Drupal\system\EventSubscriber\ThemeSettingsCacheTag
+   */
+  public function getCacheTags() {
+    return ['rendered'];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index 269b64e..06c2ba5 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -144,6 +144,9 @@ public function renderResponse(array $main_content, Request $request, RouteMatch
       }
     }
 
+    // Get the cache tags associated with the cache contexts.
+    $cache_tags = Cache::mergeTags($cache_tags, \Drupal::service('cache_contexts')->getCacheTagsForTokens($cache_contexts));
+
     // Set the generator in the HTTP header.
     list($version) = explode('.', \Drupal::VERSION, 2);
 
diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index 06f9c4f..2ba0a08 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -549,6 +549,9 @@ protected function cacheSet(array &$elements, $pre_bubbling_cid) {
     $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT;
     $cache = $this->cacheFactory->get($bin);
 
+    // Get the cache tags associated with the cache contexts.
+    $context_tags = $this->cacheContexts->getCacheTagsForTokens($elements['#cache']['contexts']);
+
     // Two-tier caching: detect different CID post-bubbling, create redirect,
     // update redirect if different set of cache contexts.
     // @see ::doRender()
@@ -686,7 +689,7 @@ protected function cacheSet(array &$elements, $pre_bubbling_cid) {
             'tags' => Cache::mergeTags($stored_cache_tags, $data['#cache']['tags']),
           ],
         ];
-        $cache->set($pre_bubbling_cid, $redirect_data, $expire, Cache::mergeTags($redirect_data['#cache']['tags'], ['rendered']));
+        $cache->set($pre_bubbling_cid, $redirect_data, $expire, Cache::mergeTags($redirect_data['#cache']['tags'], $context_tags, ['rendered']));
       }
 
       // Current cache contexts incomplete: this request only uses a subset of
@@ -707,7 +710,7 @@ protected function cacheSet(array &$elements, $pre_bubbling_cid) {
         $data['#cache']['contexts'] = $merged_cache_contexts;
       }
     }
-    $cache->set($cid, $data, $expire, Cache::mergeTags($data['#cache']['tags'], ['rendered']));
+    $cache->set($cid, $data, $expire, Cache::mergeTags($data['#cache']['tags'], $context_tags, ['rendered']));
   }
 
   /**
diff --git a/core/modules/user/src/Cache/UserRolesCacheContext.php b/core/modules/user/src/Cache/UserRolesCacheContext.php
index 0f7975d..2b61ee1 100644
--- a/core/modules/user/src/Cache/UserRolesCacheContext.php
+++ b/core/modules/user/src/Cache/UserRolesCacheContext.php
@@ -7,13 +7,15 @@
 
 namespace Drupal\user\Cache;
 
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheContextInterface;
+use Drupal\Core\Cache\ConfigurableCacheContextInterface;
 use Drupal\Core\Session\AccountInterface;
 
 /**
  * Defines the UserRolesCacheContext service, for "per role" caching.
  */
-class UserRolesCacheContext implements CacheContextInterface {
+class UserRolesCacheContext implements CacheContextInterface, ConfigurableCacheContextInterface {
 
   /**
    * Constructs a new UserRolesCacheContext service.
@@ -39,4 +41,13 @@ public function getContext() {
     return 'r.' . implode(',', $this->user->getRoles());
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * @see \Drupal\Core\Config\Entity\ConfigEntityBase::getCacheTags()
+   */
+  public function getCacheTags() {
+    return Cache::buildTags('config:user.role', $this->user->getRoles(), '.');
+  }
+
 }
