 core/includes/module.inc                           |  2 +-
 core/includes/theme.inc                            | 34 +++++++++++++
 core/lib/Drupal/Core/Access/AccessManager.php      |  2 +-
 core/lib/Drupal/Core/Access/AccessResult.php       | 14 ++++++
 core/lib/Drupal/Core/Cache/Cache.php               |  6 ++-
 core/lib/Drupal/Core/Config/Config.php             |  2 +
 core/lib/Drupal/Core/Config/ConfigBase.php         | 11 ++++
 .../Drupal/Core/Config/Entity/ConfigEntityBase.php |  7 +++
 core/lib/Drupal/Core/Entity/EntityInterface.php    |  2 +-
 core/lib/Drupal/Core/Menu/MenuLinkDefault.php      |  3 +-
 core/lib/Drupal/Core/Menu/MenuLinkTree.php         |  4 +-
 core/lib/Drupal/Core/Menu/MenuTreeStorage.php      |  8 +--
 .../Drupal/Core/Menu/StaticMenuLinkOverrides.php   |  7 +++
 .../Core/Menu/StaticMenuLinkOverridesInterface.php |  8 +++
 core/lib/Drupal/Core/Render/Element/Page.php       |  7 ++-
 core/lib/Drupal/Core/Theme/ThemeAccessCheck.php    |  4 +-
 core/lib/Drupal/Core/Theme/ThemeSettings.php       |  2 +
 core/modules/block/src/Entity/Block.php            |  2 +-
 core/modules/block/src/Tests/BlockTest.php         | 26 +++++-----
 .../block/src/Tests/BlockViewBuilderTest.php       |  6 +--
 .../src/Tests/BlockContentCacheTagsTest.php        |  2 +-
 core/modules/comment/comment.module                |  1 +
 .../comment/src/Tests/CommentCacheTagsTest.php     |  7 ++-
 .../Tests/CommentDefaultFormatterCacheTagsTest.php |  7 ++-
 .../contact/src/Access/ContactPageAccess.php       |  4 +-
 core/modules/filter/src/Tests/FilterAPITest.php    |  2 +-
 .../image/src/Tests/ImageFieldDisplayTest.php      |  2 +-
 .../menu_ui/src/Tests/MenuCacheTagsTest.php        | 10 ++--
 core/modules/node/node.module                      |  1 +
 core/modules/node/src/Tests/NodeCacheTagsTest.php  | 10 +++-
 .../src/Tests/ResponsiveImageFieldDisplayTest.php  |  8 +--
 .../search/src/Tests/SearchPageCacheTagsTest.php   |  8 +--
 core/modules/shortcut/shortcut.module              |  1 +
 .../shortcut/src/Tests/ShortcutCacheTagsTest.php   |  4 +-
 core/modules/system/src/Form/ThemeSettingsForm.php |  9 ----
 .../src/Plugin/Block/SystemBrandingBlock.php       |  3 ++
 .../system/src/Plugin/Block/SystemMenuBlock.php    |  2 +-
 .../system/src/Tests/Bootstrap/PageCacheTest.php   |  4 +-
 .../Tests/Cache/PageCacheTagsIntegrationTest.php   | 58 +++++++++++-----------
 .../src/Tests/Entity/EntityCacheTagsTestBase.php   |  2 +-
 core/modules/system/system.module                  |  1 +
 core/modules/toolbar/toolbar.module                |  2 +-
 core/modules/tour/src/Tests/TourCacheTagsTest.php  | 17 ++++---
 .../user/src/Access/RegisterAccessCheck.php        |  4 +-
 core/modules/user/src/PermissionsHash.php          |  2 +-
 core/modules/user/src/Tests/UserPictureTest.php    |  6 ---
 core/modules/user/src/Tests/UserSignatureTest.php  |  2 +-
 core/modules/user/user.module                      |  1 +
 .../views/src/Plugin/ViewsHandlerManager.php       |  2 +-
 .../views/src/Plugin/ViewsPluginManager.php        |  2 +-
 .../src/Plugin/views/cache/CachePluginBase.php     | 10 ++--
 .../src/Plugin/views/display/DisplayPluginBase.php |  3 +-
 core/modules/views/src/ViewsData.php               |  2 +-
 core/modules/views/views.module                    |  6 +--
 core/modules/views_ui/src/ViewUI.php               |  2 +-
 .../Drupal/Tests/Core/Access/AccessManagerTest.php |  2 +-
 .../Drupal/Tests/Core/Access/AccessResultTest.php  | 13 +++++
 core/tests/Drupal/Tests/Core/Config/ConfigTest.php | 21 ++++++++
 .../Config/Entity/ConfigEntityBaseUnitTest.php     |  5 +-
 .../Core/Config/Entity/ConfigEntityStorageTest.php |  8 +--
 core/themes/bartik/bartik.theme                    |  1 +
 61 files changed, 277 insertions(+), 137 deletions(-)

diff --git a/core/includes/module.inc b/core/includes/module.inc
index ff11244..66f500d 100644
--- a/core/includes/module.inc
+++ b/core/includes/module.inc
@@ -69,7 +69,7 @@ function system_list_reset() {
   // @todo Trigger an event upon module install/uninstall and theme
   //   enable/disable, and move this into an event subscriber.
   // @see https://drupal.org/node/2206347
-  Cache::invalidateTags(array('extension'));
+  Cache::invalidateTags(['config:core.extension']);
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 2c8f3fd..f1162fb 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -320,6 +320,39 @@ function drupal_find_theme_templates($cache, $extension, $path) {
 }
 
 /**
+ * Theme settings are an abomination, especially if they're used for things that
+ * fundamentally aren't theme settings, but module or template settings. What's
+ * worse is that this means that a preprocess function must itself bubble cache
+ * tags, rather than setting it on a render array and letting that bubble it;
+ * hence we end up with this monstrosity.
+ *
+ * Whenever theme_get_setting() is used in a preprocess function, this must also
+ * be called, to ensure that the rendered HTML (and the render cache items that
+ * contain this HTML) are tagged with the necessary cache tags.
+ */
+function theme_add_setting_cache_tag() {
+  $build = [
+    '#cache' => [
+      'tags' => theme_setting_get_cache_tags(),
+    ],
+  ];
+  drupal_render($build);
+}
+
+/**
+ * There's also no cheap way to figure out if it was the global theme settings
+ * or a theme- specific overriding setting that wins, so we use both.
+ *
+ * @return string[]
+ */
+function theme_setting_get_cache_tags() {
+  $theme = \Drupal::theme()->getActiveTheme()->getName();
+  return [
+    'config:' . $theme . '.settings',
+    'config:system.theme.global',
+  ];
+}
+/**
  * Retrieves a setting for the current theme or for a given theme.
  *
  * The final setting is obtained from the last value found in the following
@@ -1434,6 +1467,7 @@ function template_preprocess_page(&$variables) {
     }
   }
 
+  theme_add_setting_cache_tag();
   $variables['base_path']         = base_path();
   $variables['front_page']        = \Drupal::url('<front>');
   $variables['language']          = $language_interface;
diff --git a/core/lib/Drupal/Core/Access/AccessManager.php b/core/lib/Drupal/Core/Access/AccessManager.php
index 576ffb7..a59f697 100644
--- a/core/lib/Drupal/Core/Access/AccessManager.php
+++ b/core/lib/Drupal/Core/Access/AccessManager.php
@@ -97,7 +97,7 @@ public function checkNamedRoute($route_name, array $parameters = array(), Accoun
     }
     catch (RouteNotFoundException $e) {
       // Cacheable until extensions change.
-      $result = AccessResult::forbidden()->addCacheTags(array('extension'));
+      $result = AccessResult::forbidden()->addCacheTags(['config:core.extension']);
       return $return_as_object ? $result : $result->isAllowed();
     }
     catch (ParamNotConvertedException $e) {
diff --git a/core/lib/Drupal/Core/Access/AccessResult.php b/core/lib/Drupal/Core/Access/AccessResult.php
index 329d50d..9ea5783 100644
--- a/core/lib/Drupal/Core/Access/AccessResult.php
+++ b/core/lib/Drupal/Core/Access/AccessResult.php
@@ -8,6 +8,7 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableInterface;
+use Drupal\Core\Config\ConfigBase;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Session\AccountInterface;
 
@@ -361,6 +362,19 @@ public function cacheUntilEntityChanges(EntityInterface $entity) {
   }
 
   /**
+   * Convenience method, adds the configuration object's cache tag.
+   *
+   * @param \Drupal\Core\Config\ConfigBase $configuration
+   *   The entity whose cache tag to set on the access result.
+   *
+   * @return $this
+   */
+  public function cacheUntilConfigurationChanges(ConfigBase $configuration) {
+    $this->addCacheTags($configuration->getCacheTags());
+    return $this;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function orIf(AccessResultInterface $other) {
diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php
index ddf444e..718f616 100644
--- a/core/lib/Drupal/Core/Cache/Cache.php
+++ b/core/lib/Drupal/Core/Cache/Cache.php
@@ -81,14 +81,16 @@ public static function validateTags(array $tags) {
    *   A prefix string.
    * @param array $suffixes
    *   An array of suffixes. Will be cast to strings.
+   * @param $glue
+   *   A string to be used as glue for concatenation. Defaults to a colon.
    *
    * @return string[]
    *   An array of cache tags.
    */
-  public static function buildTags($prefix, array $suffixes) {
+  public static function buildTags($prefix, array $suffixes, $glue = ':') {
     $tags = [];
     foreach ($suffixes as $suffix) {
-      $tags[] = $prefix . ':' . $suffix;
+      $tags[] = $prefix . $glue . $suffix;
     }
     return $tags;
   }
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index d354799..a4004a8 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Cache\Cache;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
@@ -223,6 +224,7 @@ public function save() {
     }
 
     $this->storage->write($this->name, $this->data);
+    Cache::invalidateTags($this->getCacheTags());
     $this->isNew = FALSE;
     $this->eventDispatcher->dispatch(ConfigEvents::SAVE, new ConfigCrudEvent($this));
     $this->originalData = $this->data;
diff --git a/core/lib/Drupal/Core/Config/ConfigBase.php b/core/lib/Drupal/Core/Config/ConfigBase.php
index f259dc7..e94db9b 100644
--- a/core/lib/Drupal/Core/Config/ConfigBase.php
+++ b/core/lib/Drupal/Core/Config/ConfigBase.php
@@ -262,4 +262,15 @@ public function merge(array $data_to_merge) {
     $this->setData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE));
     return $this;
   }
+
+  /**
+   * The unique cache tag associated with this configuration object.
+   *
+   * @return string[]
+   *   An array of cache tags.
+   */
+  public function getCacheTags() {
+    return ['config:' . $this->name];
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
index ad5f0d1..6e8866b 100644
--- a/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
+++ b/core/lib/Drupal/Core/Config/Entity/ConfigEntityBase.php
@@ -372,6 +372,13 @@ public function link($text = NULL, $rel = 'edit-form', array $options = []) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return ['config:' . $this->getConfigDependencyName()];
+  }
+
+  /**
    * Overrides \Drupal\Core\Entity\DependencyTrait:addDependency().
    *
    * Note that this function should only be called from implementations of
diff --git a/core/lib/Drupal/Core/Entity/EntityInterface.php b/core/lib/Drupal/Core/Entity/EntityInterface.php
index 674078d..a35eea6 100644
--- a/core/lib/Drupal/Core/Entity/EntityInterface.php
+++ b/core/lib/Drupal/Core/Entity/EntityInterface.php
@@ -399,7 +399,7 @@ public function getTypedData();
   /**
    * The unique cache tag associated with this entity.
    *
-   * @return array
+   * @return string[]
    *   An array of cache tags.
    */
   public function getCacheTags();
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkDefault.php b/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
index e118824..6b1d55c 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkDefault.php
@@ -95,8 +95,7 @@ public function getDescription() {
    */
   public function isResettable() {
     // The link can be reset if it has an override.
-    // @todo This will be cacheable after https://www.drupal.org/node/2040135.
-    return AccessResult::allowedIf($this->staticOverride->loadOverride($this->getPluginId()))->setCacheable(FALSE);
+    return AccessResult::allowedIf($this->staticOverride->loadOverride($this->getPluginId()))->cacheUntilConfigurationChanges($this->staticOverride->getCacheTags());
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkTree.php b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
index 61c60c2..8851b27 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkTree.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkTree.php
@@ -125,7 +125,7 @@ public function getCurrentRouteMenuTreeParameters($menu_name) {
           // expanded.
           ->addExpandedParents($this->treeStorage->getExpanded($menu_name, $active_trail));
 
-        $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('menu:' . $menu_name));
+        $this->cache->set($cid, $parameters, CacheBackendInterface::CACHE_PERMANENT, array('config:system.menu.' . $menu_name));
       }
       $this->cachedCurrentRouteParameters[$menu_name] = $parameters;
     }
@@ -255,7 +255,7 @@ public function build(array $tree, $level = 0) {
       $build['#theme'] = 'menu__' . strtr($menu_name, '-', '_');
       $build['#items'] = $items;
       // Set cache tag.
-      $build['#cache']['tags'][] = 'menu:' . $menu_name;
+      $build['#cache']['tags'][] = 'config:system.menu.' . $menu_name;
       return $build;
     }
     else {
diff --git a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
index 0898091..941ac41 100644
--- a/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
+++ b/core/lib/Drupal/Core/Menu/MenuTreeStorage.php
@@ -180,7 +180,7 @@ public function rebuild(array $definitions) {
     $this->resetDefinitions();
     $affected_menus = $this->getMenuNames() + $before_menus;
     // Invalidate any cache tagged with any menu name.
-    $cache_tags = Cache::buildTags('menu', $affected_menus);
+    $cache_tags = Cache::buildTags('config:system.menu.', $affected_menus, '');
     Cache::invalidateTags($cache_tags);
     $this->resetDefinitions();
     // Every item in the cache bin should have one of the menu cache tags but it
@@ -241,7 +241,7 @@ protected function safeExecuteSelect(SelectInterface $query) {
   public function save(array $link) {
     $affected_menus = $this->doSave($link);
     $this->resetDefinitions();
-    $cache_tags = Cache::buildTags('menu', $affected_menus);
+    $cache_tags = Cache::buildTags('config:system.menu.', $affected_menus, '');
     Cache::invalidateTags($cache_tags);
     return $affected_menus;
   }
@@ -421,7 +421,7 @@ public function delete($id) {
       $this->updateParentalStatus($item);
       // Many children may have moved.
       $this->resetDefinitions();
-      Cache::invalidateTags(array('menu:' . $item['menu_name']));
+      Cache::invalidateTags(array('config:system.menu.' . $item['menu_name']));
     }
   }
 
@@ -822,7 +822,7 @@ public function loadTreeData($menu_name, MenuTreeParameters $parameters) {
       $data['tree'] = $this->doBuildTreeData($links, $parameters->activeTrail, $parameters->minDepth);
       $data['definitions'] = array();
       $data['route_names'] = $this->collectRoutesAndDefinitions($data['tree'], $data['definitions']);
-      $this->menuCacheBackend->set($tree_cid, $data, Cache::PERMANENT, array('menu:' . $menu_name));
+      $this->menuCacheBackend->set($tree_cid, $data, Cache::PERMANENT, ['config:system.menu.' . $menu_name]);
       // The definitions were already added to $this->definitions in
       // $this->doBuildTreeData()
       unset($data['definitions']);
diff --git a/core/lib/Drupal/Core/Menu/StaticMenuLinkOverrides.php b/core/lib/Drupal/Core/Menu/StaticMenuLinkOverrides.php
index ebde906..3f30dfc 100644
--- a/core/lib/Drupal/Core/Menu/StaticMenuLinkOverrides.php
+++ b/core/lib/Drupal/Core/Menu/StaticMenuLinkOverrides.php
@@ -144,6 +144,13 @@ public function saveOverride($id, array $definition) {
   }
 
   /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return $this->getConfig()->getCacheTags();
+  }
+
+  /**
    * Encodes the ID by replacing dots with double underscores.
    *
    * This is done because config schema uses dots for its internal type
diff --git a/core/lib/Drupal/Core/Menu/StaticMenuLinkOverridesInterface.php b/core/lib/Drupal/Core/Menu/StaticMenuLinkOverridesInterface.php
index 43c4416..f3f429a 100644
--- a/core/lib/Drupal/Core/Menu/StaticMenuLinkOverridesInterface.php
+++ b/core/lib/Drupal/Core/Menu/StaticMenuLinkOverridesInterface.php
@@ -84,4 +84,12 @@ public function loadMultipleOverrides(array $ids);
    */
   public function saveOverride($id, array $definition);
 
+  /**
+   * The unique cache tag associated with this menu link override.
+   *
+   * @return string[]
+   *   An array of cache tags.
+   */
+  public function getCacheTags();
+
 }
diff --git a/core/lib/Drupal/Core/Render/Element/Page.php b/core/lib/Drupal/Core/Render/Element/Page.php
index 0d8b777..9d7aca8 100644
--- a/core/lib/Drupal/Core/Render/Element/Page.php
+++ b/core/lib/Drupal/Core/Render/Element/Page.php
@@ -40,8 +40,11 @@ public function getInfo() {
    * @return array
    */
   public static function preRenderPage($element) {
-    $element['#cache']['tags'][] = 'theme:' . \Drupal::theme()->getActiveTheme()->getName();
-    $element['#cache']['tags'][] = 'theme_global_settings';
+    // Assign the cache tag for the theme-specific settings. Instead of using
+    // yet another cache tag (such as 'theme:<theme name>') to tag HTML page
+    // responses that are rendered using a certain theme, we reuse the theme
+    // settings simple config cache tag instead.
+    $element['#cache']['tags'][] = 'config:' . \Drupal::theme()->getActiveTheme()->getName() . '.settings';
     return $element;
   }
 
diff --git a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
index 78ffd26..38c2e31 100644
--- a/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
+++ b/core/lib/Drupal/Core/Theme/ThemeAccessCheck.php
@@ -25,8 +25,8 @@ class ThemeAccessCheck implements AccessInterface {
    *   The access result.
    */
   public function access($theme) {
-    // Cacheable until the theme is modified.
-    return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(array('theme:' . $theme));
+    // Cacheable until the theme settings are modified.
+    return AccessResult::allowedIf($this->checkAccess($theme))->addCacheTags(['config:' . $theme . '.settings']);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Theme/ThemeSettings.php b/core/lib/Drupal/Core/Theme/ThemeSettings.php
index f1ad829..a43d3f8 100644
--- a/core/lib/Drupal/Core/Theme/ThemeSettings.php
+++ b/core/lib/Drupal/Core/Theme/ThemeSettings.php
@@ -17,6 +17,8 @@
  * not persisted.
  *
  * @see theme_get_setting()
+ * @see theme_add_setting_cache_tag()
+ * @see theme_setting_get_cache_tags()
  */
 class ThemeSettings extends ConfigBase {
 
diff --git a/core/modules/block/src/Entity/Block.php b/core/modules/block/src/Entity/Block.php
index 0edc961..4b1ffca 100644
--- a/core/modules/block/src/Entity/Block.php
+++ b/core/modules/block/src/Entity/Block.php
@@ -212,7 +212,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
    * the associated theme's cache tag.
    */
   public function getCacheTags() {
-    return Cache::mergeTags(parent::getCacheTags(), ['theme:' . $this->theme]);
+    return Cache::mergeTags(parent::getCacheTags(), ['config:' . $this->theme . '.settings']);
   }
 
   /**
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 809f289..d390b00 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -327,10 +327,10 @@ public function testBlockCacheTags() {
     $cid = implode(':', $cid_parts);
     $cache_entry = \Drupal::cache('render')->get($cid);
     $expected_cache_tags = array(
-      'theme:classy',
-      'theme_global_settings',
+      'config:classy.settings',
+      'config:system.theme.global',
       'block_view',
-      'block:powered',
+      'config:block.block.powered',
       'block_plugin:system_powered_by_block',
       'rendered',
     );
@@ -339,8 +339,8 @@ public function testBlockCacheTags() {
     $cache_entry = \Drupal::cache('render')->get('entity_view:block:powered:en:classy');
     $expected_cache_tags = array(
       'block_view',
-      'block:powered',
-      'theme:classy',
+      'config:block.block.powered',
+      'config:classy.settings',
       'block_plugin:system_powered_by_block',
       'rendered',
     );
@@ -369,11 +369,11 @@ public function testBlockCacheTags() {
     $cid = implode(':', $cid_parts);
     $cache_entry = \Drupal::cache('render')->get($cid);
     $expected_cache_tags = array(
-      'theme:classy',
-      'theme_global_settings',
+      'config:classy.settings',
+      'config:system.theme.global',
       'block_view',
-      'block:powered-2',
-      'block:powered',
+      'config:block.block.powered',
+      'config:block.block.powered-2',
       'block_plugin:system_powered_by_block',
       'rendered',
     );
@@ -381,8 +381,8 @@ public function testBlockCacheTags() {
     $this->assertEqual($cache_entry->tags, $expected_cache_tags);
     $expected_cache_tags = array(
       'block_view',
-      'block:powered',
-      'theme:classy',
+      'config:block.block.powered',
+      'config:classy.settings',
       'block_plugin:system_powered_by_block',
       'rendered',
     );
@@ -391,8 +391,8 @@ public function testBlockCacheTags() {
     $this->assertIdentical($cache_entry->tags, $expected_cache_tags);
     $expected_cache_tags = array(
       'block_view',
-      'block:powered-2',
-      'theme:classy',
+      'config:block.block.powered-2',
+      'config:classy.settings',
       'block_plugin:system_powered_by_block',
       'rendered',
     );
diff --git a/core/modules/block/src/Tests/BlockViewBuilderTest.php b/core/modules/block/src/Tests/BlockViewBuilderTest.php
index 2ddbcab..4c45933 100644
--- a/core/modules/block/src/Tests/BlockViewBuilderTest.php
+++ b/core/modules/block/src/Tests/BlockViewBuilderTest.php
@@ -215,7 +215,7 @@ public function testBlockViewBuilderAlter() {
     $request->setMethod('GET');
 
     $default_keys = array('entity_view', 'block', 'test_block', 'en', 'cache_context.theme');
-    $default_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache');
+    $default_tags = array('block_view', 'config:block.block.test_block', 'config:stark.settings', 'block_plugin:test_cache');
 
     // Advanced: cached block, but an alter hook adds an additional cache key.
     $this->setBlockCacheConfig(array(
@@ -230,7 +230,7 @@ public function testBlockViewBuilderAlter() {
     $this->assertIdentical(drupal_render($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
-    $expected_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', 'rendered');
+    $expected_tags = array_merge($default_tags, ['rendered']);
     sort($expected_tags);
     $this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
     $this->container->get('cache.render')->delete($cid);
@@ -246,7 +246,7 @@ public function testBlockViewBuilderAlter() {
     $this->assertIdentical(drupal_render($build), '');
     $cache_entry = $this->container->get('cache.render')->get($cid);
     $this->assertTrue($cache_entry, 'The block render element has been cached with the expected cache ID.');
-    $expected_tags = array('block_view', 'block:test_block', 'theme:stark', 'block_plugin:test_cache', $alter_add_tag, 'rendered');
+    $expected_tags = array_merge($default_tags, [$alter_add_tag, 'rendered']);
     sort($expected_tags);
     $this->assertIdentical($cache_entry->tags, $expected_tags, 'The block render element has been cached with the expected cache tags.');
     $this->container->get('cache.render')->delete($cid);
diff --git a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
index 1ea2156..b02086d 100644
--- a/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
+++ b/core/modules/block_content/src/Tests/BlockContentCacheTagsTest.php
@@ -55,7 +55,7 @@ protected function createEntity() {
    * Each comment must have a comment body, which always has a text format.
    */
   protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
-    return array('filter_format:plain_text');
+    return ['config:filter.format.plain_text'];
   }
 
 }
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index d9f9674..f3724a6 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -708,6 +708,7 @@ function template_preprocess_comment(&$variables) {
     $variables['changed'] = format_date($comment->getChangedTime());
   }
 
+  theme_add_setting_cache_tag();
   if (theme_get_setting('features.comment_user_picture')) {
     // To change user picture settings (e.g., image style), edit the 'compact'
     // view mode on the User entity.
diff --git a/core/modules/comment/src/Tests/CommentCacheTagsTest.php b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
index 5da5555..0b8a26e 100644
--- a/core/modules/comment/src/Tests/CommentCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentCacheTagsTest.php
@@ -82,13 +82,18 @@ protected function createEntity() {
    * {@inheritdoc}
    *
    * Each comment must have a comment body, which always has a text format.
+   *
+   * Plus, template_preprocess_comment() depends on theme settings.
    */
   protected function getAdditionalCacheTagsForEntity(EntityInterface $entity) {
     /** @var \Drupal\comment\CommentInterface $entity */
     return array(
-      'filter_format:plain_text',
+      'config:filter.format.plain_text',
       'user:' . $entity->getOwnerId(),
       'user_view',
+      // Cache tags added by template_preprocess_comment().
+      'config:classy.settings',
+      'config:system.theme.global',
     );
   }
 
diff --git a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
index 5e4359d..e8ed806 100644
--- a/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
+++ b/core/modules/comment/src/Tests/CommentDefaultFormatterCacheTagsTest.php
@@ -107,12 +107,15 @@ public function testCacheTags() {
       'comment_list',
       'comment_view',
       'comment:' . $comment->id(),
-      'filter_format:plain_text',
+      'config:filter.format.plain_text',
       'user_view',
       'user:2',
+      // Cache tags added by template_preprocess_comment().
+      'config:core.settings',
+      'config:system.theme.global',
     );
     sort($expected_cache_tags);
-    $this->assertEqual($build['#cache']['tags'], $expected_cache_tags, 'The test entity has the expected cache tags when it has comments.');
+    $this->assertEqual($build['#cache']['tags'], $expected_cache_tags);
   }
 
 }
diff --git a/core/modules/contact/src/Access/ContactPageAccess.php b/core/modules/contact/src/Access/ContactPageAccess.php
index b0574da..554b368 100644
--- a/core/modules/contact/src/Access/ContactPageAccess.php
+++ b/core/modules/contact/src/Access/ContactPageAccess.php
@@ -91,7 +91,9 @@ public function access(UserInterface $user, AccountInterface $account) {
     }
     // If the requested user did not save a preference yet, deny access if the
     // configured default is disabled.
-    else if (!$this->configFactory->get('contact.settings')->get('user_default_enabled')) {
+    $contact_settings = $this->configFactory->get('contact.settings');
+    $access->cacheUntilConfigurationChanges($contact_settings);
+    if (!$contact_settings->get('user_default_enabled')) {
       return $access;
     }
 
diff --git a/core/modules/filter/src/Tests/FilterAPITest.php b/core/modules/filter/src/Tests/FilterAPITest.php
index 2808fc1..dae7a93 100644
--- a/core/modules/filter/src/Tests/FilterAPITest.php
+++ b/core/modules/filter/src/Tests/FilterAPITest.php
@@ -247,7 +247,7 @@ function testProcessedTextElement() {
     $this->assertEqual($expected_assets, $build['#attached'], 'Expected assets present');
     $expected_cache_tags = array(
       // The cache tag set by the processed_text element itself.
-      'filter_format:element_test',
+      'config:filter.format.element_test',
       // The cache tags set by the filter_test_cache_tags filter.
       'foo:bar',
       'foo:baz',
diff --git a/core/modules/image/src/Tests/ImageFieldDisplayTest.php b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
index b73c06f..f8820fe 100644
--- a/core/modules/image/src/Tests/ImageFieldDisplayTest.php
+++ b/core/modules/image/src/Tests/ImageFieldDisplayTest.php
@@ -173,7 +173,7 @@ function _testImageFieldFormatters($scheme) {
     $default_output = drupal_render($image_style);
     $this->drupalGet('node/' . $nid);
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    $this->assertTrue(in_array('image_style:thumbnail', $cache_tags));
+    $this->assertTrue(in_array('config:image.style.thumbnail', $cache_tags));
     $this->assertRaw($default_output, 'Image style thumbnail formatter displaying correctly on full node view.');
 
     if ($scheme == 'private') {
diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
index ba19651..feac980 100644
--- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
@@ -48,13 +48,13 @@ public function testMenuBlock() {
 
     // Verify a cache hit, but also the presence of the correct cache tags.
     $expected_tags = array(
-      'theme:classy',
-      'theme_global_settings',
+      'config:classy.settings',
+      'config:system.theme.global',
       'rendered',
       'block_view',
-      'block:' . $block->id(),
+      'config:block.block.' . $block->id(),
       'block_plugin:system_menu_block__llama',
-      'menu:llama',
+      'config:system.menu.llama',
     );
     $this->verifyPageCache($path, 'HIT', $expected_tags);
 
@@ -106,7 +106,7 @@ public function testMenuBlock() {
     $this->verifyPageCache($path, 'MISS');
 
     // Verify a cache hit.
-    $this->verifyPageCache($path, 'HIT', array('rendered', 'theme:classy', 'theme_global_settings'));
+    $this->verifyPageCache($path, 'HIT', array('rendered', 'config:classy.settings', 'config:system.theme.global'));
   }
 
 }
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 918e738..0e709a0 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -604,6 +604,7 @@ function template_preprocess_node(&$variables) {
   $variables['author_attributes'] = new Attribute();
   $variables['display_submitted'] = $node_type->displaySubmitted();
   if ($variables['display_submitted']) {
+    theme_add_setting_cache_tag();
     if (theme_get_setting('features.node_user_picture')) {
       // To change user picture settings (e.g. image style), edit the 'compact'
       // view mode on the User entity. Note that the 'compact' view mode might
diff --git a/core/modules/node/src/Tests/NodeCacheTagsTest.php b/core/modules/node/src/Tests/NodeCacheTagsTest.php
index cd46f79..67753eb 100644
--- a/core/modules/node/src/Tests/NodeCacheTagsTest.php
+++ b/core/modules/node/src/Tests/NodeCacheTagsTest.php
@@ -46,9 +46,17 @@ protected function createEntity() {
    * {@inheritdoc}
    *
    * Each node must have an author.
+   *
+   * Plus, template_preprocess_node() depends on theme settings.
    */
   protected function getAdditionalCacheTagsForEntity(EntityInterface $node) {
-    return array('user:' . $node->getOwnerId(), 'user_view');
+    return [
+      'user:' . $node->getOwnerId(),
+      'user_view',
+      // Cache tags added by template_preprocess_comment().
+      'config:classy.settings',
+      'config:system.theme.global',
+    ];
   }
 
 }
diff --git a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php
index 41e4f1c..09efc57 100644
--- a/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php
+++ b/core/modules/responsive_image/src/Tests/ResponsiveImageFieldDisplayTest.php
@@ -202,12 +202,12 @@ protected function doTestResponsiveImageFieldFormatters($scheme, $empty_styles =
     $this->assertRaw('media="(min-width: 560px)"');
     $this->assertRaw('media="(min-width: 851px)"');
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    $this->assertTrue(in_array('responsive_image_mapping:mapping_one', $cache_tags));
+    $this->assertTrue(in_array('config:responsive_image.mappings.mapping_one', $cache_tags));
     if (!$empty_styles) {
-      $this->assertTrue(in_array('image_style:thumbnail', $cache_tags));
-      $this->assertTrue(in_array('image_style:medium', $cache_tags));
+      $this->assertTrue(in_array('config:image.style.thumbnail', $cache_tags));
+      $this->assertTrue(in_array('config:image.style.medium', $cache_tags));
     }
-    $this->assertTrue(in_array('image_style:large', $cache_tags));
+    $this->assertTrue(in_array('config:image.style.large', $cache_tags));
 
     // Test the fallback image style.
     $large_style = entity_load('image_style', 'large');
diff --git a/core/modules/search/src/Tests/SearchPageCacheTagsTest.php b/core/modules/search/src/Tests/SearchPageCacheTagsTest.php
index d5aa9ec..7779055 100644
--- a/core/modules/search/src/Tests/SearchPageCacheTagsTest.php
+++ b/core/modules/search/src/Tests/SearchPageCacheTagsTest.php
@@ -34,25 +34,25 @@ function testSearchText() {
     // Initial page for searching nodes.
     $this->drupalGet('search/node');
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    $this->assertTrue(in_array('search_page:node_search', $cache_tags));
+    $this->assertTrue(in_array('config:search.page.node_search', $cache_tags));
 
     // Node search results.
     $edit = array();
     $edit['keys'] = 'bike shed';
     $this->drupalPostForm('search/node', $edit, t('Search'));
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    $this->assertTrue(in_array('search_page:node_search', $cache_tags));
+    $this->assertTrue(in_array('config:search.page.node_search', $cache_tags));
 
     // Initial page for searching users.
     $this->drupalGet('search/user');
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    $this->assertTrue(in_array('search_page:user_search', $cache_tags));
+    $this->assertTrue(in_array('config:search.page.user_search', $cache_tags));
 
     // User search results.
     $edit['keys'] = $this->searching_user->getUsername();
     $this->drupalPostForm('search/user', $edit, t('Search'));
     $cache_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    $this->assertTrue(in_array('search_page:user_search', $cache_tags));
+    $this->assertTrue(in_array('config:search.page.user_search', $cache_tags));
   }
 
 }
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index ba9c43d..526f0da 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -330,6 +330,7 @@ function shortcut_preprocess_page(&$variables) {
       $route_parameters = array('shortcut' => $shortcut_id);
     }
 
+    theme_add_setting_cache_tag();
     if (theme_get_setting('third_party_settings.shortcut.module_link')) {
       $variables['title_suffix']['add_or_remove_shortcut'] = array(
         '#attached' => array(
diff --git a/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php b/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php
index 98b0904..2fd7385 100644
--- a/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutCacheTagsTest.php
@@ -58,11 +58,11 @@ protected function createEntity() {
    */
   public function testEntityCreation() {
     // Create a cache entry that is tagged with a shortcut set cache tag.
-    $cache_tags = array('shortcut_set:default');
+    $cache_tags = ['config:shortcut.set.default'];
     \Drupal::cache('render')->set('foo', 'bar', \Drupal\Core\Cache\CacheBackendInterface::CACHE_PERMANENT, $cache_tags);
 
     // Verify a cache hit.
-    $this->verifyRenderCache('foo', array('shortcut_set:default'));
+    $this->verifyRenderCache('foo', $cache_tags);
 
     // Now create a shortcut entity in that shortcut set.
     $this->createEntity();
diff --git a/core/modules/system/src/Form/ThemeSettingsForm.php b/core/modules/system/src/Form/ThemeSettingsForm.php
index 8e72246..8bb150a 100644
--- a/core/modules/system/src/Form/ThemeSettingsForm.php
+++ b/core/modules/system/src/Form/ThemeSettingsForm.php
@@ -419,15 +419,6 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     }
 
     theme_settings_convert_to_config($values, $config)->save();
-
-    // Invalidate either the theme-specific cache tag or the global theme
-    // settings cache tag, depending on whose settings were actually changed.
-    if (isset($values['theme'])) {
-      Cache::invalidateTags(array('theme:' . $values['theme']));
-    }
-    else {
-      Cache::invalidateTags(array('theme_global_settings'));
-    }
   }
 
   /**
diff --git a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
index 6d10aff..dfddabf 100644
--- a/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemBrandingBlock.php
@@ -170,6 +170,9 @@ public function build() {
       '#uri' => $logo['url'],
       '#alt' => t('Home'),
       '#access' => $this->configuration['use_site_logo'],
+      '#cache' => [
+        'tags' => theme_setting_get_cache_tags(),
+      ],
     );
 
     $build['site_name'] = array(
diff --git a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
index 16af329..57aceef 100644
--- a/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemMenuBlock.php
@@ -190,7 +190,7 @@ public function getCacheTags() {
     // menu block must also be re-rendered for that user, because maybe a menu
     // link that is accessible for that user has been added.
     $cache_tags = parent::getCacheTags();
-    $cache_tags[] = 'menu:' . $this->getDerivativeId();
+    $cache_tags[] = 'config:system.menu.' . $this->getDerivativeId();
     return $cache_tags;
   }
 
diff --git a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
index 9a11d1b..a4b3611 100644
--- a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
+++ b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
@@ -62,11 +62,11 @@ function testPageCacheTags() {
     $cache_entry = \Drupal::cache('render')->get($cid);
     sort($cache_entry->tags);
     $expected_tags = array(
+      'config:classy.settings',
+      'config:system.theme.global',
       'pre_render',
       'rendered',
       'system_test_cache_tags_page',
-      'theme:classy',
-      'theme_global_settings',
     );
     $this->assertIdentical($cache_entry->tags, $expected_tags);
 
diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
index 0109bf1..97639bc 100644
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
@@ -69,16 +69,16 @@ function testPageCacheTags() {
     // Full node page 1.
     $this->verifyPageCacheTags('node/' . $node_1->id(), array(
       'rendered',
-      'theme:bartik',
-      'theme_global_settings',
+      'config:bartik.settings',
+      'config:system.theme.global',
       'block_view',
-      'block:bartik_content',
-      'block:bartik_tools',
-      'block:bartik_login',
-      'block:bartik_footer',
-      'block:bartik_powered',
-      'block:bartik_main_menu',
-      'block:bartik_account_menu',
+      'config:block.block.bartik_content',
+      'config:block.block.bartik_tools',
+      'config:block.block.bartik_login',
+      'config:block.block.bartik_footer',
+      'config:block.block.bartik_powered',
+      'config:block.block.bartik_main_menu',
+      'config:block.block.bartik_account_menu',
       'block_plugin:system_main_block',
       'block_plugin:system_menu_block__account',
       'block_plugin:system_menu_block__main',
@@ -89,27 +89,27 @@ function testPageCacheTags() {
       'node_view',
       'node:' . $node_1->id(),
       'user:' . $author_1->id(),
-      'filter_format:basic_html',
-      'menu:account',
-      'menu:tools',
-      'menu:footer',
-      'menu:main',
+      'config:filter.format.basic_html',
+      'config:system.menu.account',
+      'config:system.menu.tools',
+      'config:system.menu.footer',
+      'config:system.menu.main',
     ));
 
     // Full node page 2.
     $this->verifyPageCacheTags('node/' . $node_2->id(), array(
       'rendered',
-      'theme:bartik',
-      'theme_global_settings',
+      'config:bartik.settings',
+      'config:system.theme.global',
       'block_view',
-      'block:bartik_content',
-      'block:bartik_tools',
-      'block:bartik_login',
-      'block:' . $block->id(),
-      'block:bartik_footer',
-      'block:bartik_powered',
-      'block:bartik_main_menu',
-      'block:bartik_account_menu',
+      'config:block.block.bartik_content',
+      'config:block.block.bartik_tools',
+      'config:block.block.bartik_login',
+      'config:block.block.' . $block->id(),
+      'config:block.block.bartik_footer',
+      'config:block.block.bartik_powered',
+      'config:block.block.bartik_main_menu',
+      'config:block.block.bartik_account_menu',
       'block_plugin:system_main_block',
       'block_plugin:system_menu_block__account',
       'block_plugin:system_menu_block__main',
@@ -121,11 +121,11 @@ function testPageCacheTags() {
       'node_view',
       'node:' . $node_2->id(),
       'user:' . $author_2->id(),
-      'filter_format:full_html',
-      'menu:account',
-      'menu:tools',
-      'menu:footer',
-      'menu:main',
+      'config:filter.format.full_html',
+      'config:system.menu.account',
+      'config:system.menu.tools',
+      'config:system.menu.footer',
+      'config:system.menu.main',
     ));
   }
 
diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
index 2bd830b..145daba 100644
--- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
@@ -283,7 +283,7 @@ public function testReferencedEntity() {
     $nonempty_entity_listing_path = 'entity_test/list_labels_alphabetically/' . $entity_type;
 
     $render_cache_tags = array('rendered');
-    $theme_cache_tags = array('theme:classy', 'theme_global_settings');
+    $theme_cache_tags = array('config:classy.settings', 'config:system.theme.global');
 
     $view_cache_tag = array();
     if ($this->entity->getEntityType()->hasHandlerClass('view_builder')) {
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 8f2a8ab..1d9b1eb 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -530,6 +530,7 @@ function system_page_attachments(array &$page) {
   }
 
   // Attach favicon.
+  theme_add_setting_cache_tag();
   if (theme_get_setting('features.favicon')) {
     $favicon = theme_get_setting('favicon.url');
     $type = theme_get_setting('favicon.mimetype');
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index 7b75e89..50704d0 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -314,7 +314,7 @@ function _toolbar_get_subtrees_hash($langcode) {
     // Clear the cache when the 'locale' tag is deleted. This ensures a fresh
     // subtrees rendering when string translations are made.
     $role_list_cache_tags = \Drupal::entityManager()->getDefinition('user_role')->getListCacheTags();
-    \Drupal::cache('toolbar')->set($cid, $hash, Cache::PERMANENT, Cache::mergeTags(array('user:' . $uid, 'locale', 'menu:admin'), $role_list_cache_tags));
+    \Drupal::cache('toolbar')->set($cid, $hash, Cache::PERMANENT, Cache::mergeTags(array('user:' . $uid, 'locale', 'config:system.menu.admin'), $role_list_cache_tags));
   }
   return $hash;
 }
diff --git a/core/modules/tour/src/Tests/TourCacheTagsTest.php b/core/modules/tour/src/Tests/TourCacheTagsTest.php
index 085477a..df15a4b 100644
--- a/core/modules/tour/src/Tests/TourCacheTagsTest.php
+++ b/core/modules/tour/src/Tests/TourCacheTagsTest.php
@@ -48,12 +48,12 @@ public function testRenderedTour() {
     $this->verifyPageCache($path, 'MISS');
 
     // Verify a cache hit, but also the presence of the correct cache tags.
-    $expected_tags = array(
-      'theme:classy',
-      'theme_global_settings',
-      'tour:tour-test',
+    $expected_tags = [
+      'config:classy.settings',
+      'config:system.theme.global',
+      'config:tour.tour.tour-test',
       'rendered',
-    );
+    ];
     $this->verifyPageCache($path, 'HIT', $expected_tags);
 
     // Verify that after modifying the tour, there is a cache miss.
@@ -70,7 +70,12 @@ public function testRenderedTour() {
     $this->verifyPageCache($path, 'MISS');
 
     // Verify a cache hit.
-    $this->verifyPageCache($path, 'HIT', array('rendered', 'theme:classy', 'theme_global_settings'));
+    $expected_tags = [
+      'config:classy.settings',
+      'config:system.theme.global',
+      'rendered',
+    ];
+    $this->verifyPageCache($path, 'HIT', $expected_tags);
   }
 
 }
diff --git a/core/modules/user/src/Access/RegisterAccessCheck.php b/core/modules/user/src/Access/RegisterAccessCheck.php
index 0f0a3f5..ee0e5be 100644
--- a/core/modules/user/src/Access/RegisterAccessCheck.php
+++ b/core/modules/user/src/Access/RegisterAccessCheck.php
@@ -26,7 +26,7 @@ class RegisterAccessCheck implements AccessInterface {
    *   The access result.
    */
   public function access(AccountInterface $account) {
-    // @todo cacheable per role once https://www.drupal.org/node/2040135 lands.
-    return AccessResult::allowedIf($account->isAnonymous() && \Drupal::config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY)->setCacheable(FALSE);
+    $user_settings = \Drupal::config('user.settings');
+    return AccessResult::allowedIf($account->isAnonymous() && $user_settings->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY)->cacheUntilConfigurationChanges($user_settings);
   }
 }
diff --git a/core/modules/user/src/PermissionsHash.php b/core/modules/user/src/PermissionsHash.php
index 3f72645..57f0646 100644
--- a/core/modules/user/src/PermissionsHash.php
+++ b/core/modules/user/src/PermissionsHash.php
@@ -59,7 +59,7 @@ public function generate(AccountInterface $account) {
     }
     else {
       $permissions_hash = $this->doGenerate($sorted_roles);
-      $tags = Cache::buildTags('user_role', $sorted_roles);
+      $tags = Cache::buildTags('config:user.role.', $sorted_roles, '');
       $this->cache->set("user_permissions_hash:$role_list", $permissions_hash, Cache::PERMANENT, $tags);
     }
 
diff --git a/core/modules/user/src/Tests/UserPictureTest.php b/core/modules/user/src/Tests/UserPictureTest.php
index 639c382..d453b85 100644
--- a/core/modules/user/src/Tests/UserPictureTest.php
+++ b/core/modules/user/src/Tests/UserPictureTest.php
@@ -103,9 +103,6 @@ function testPictureOnNodeComment() {
       ->set('features.comment_user_picture', TRUE)
       ->save();
 
-    // @todo Remove when https://www.drupal.org/node/2040135 lands.
-    Cache::invalidateTags(['rendered']);
-
     $edit = array(
       'comment_body[0][value]' => $this->randomString(),
     );
@@ -118,9 +115,6 @@ function testPictureOnNodeComment() {
       ->set('features.comment_user_picture', FALSE)
       ->save();
 
-    // @todo Remove when https://www.drupal.org/node/2040135 lands.
-    Cache::invalidateTags(['rendered']);
-
     $this->drupalGet('node/' . $node->id());
     $this->assertNoRaw(file_uri_target($file->getFileUri()), 'User picture not found on node and comment.');
   }
diff --git a/core/modules/user/src/Tests/UserSignatureTest.php b/core/modules/user/src/Tests/UserSignatureTest.php
index 71f3148..3e5007b 100644
--- a/core/modules/user/src/Tests/UserSignatureTest.php
+++ b/core/modules/user/src/Tests/UserSignatureTest.php
@@ -128,7 +128,7 @@ function testUserSignature() {
     $this->assertRaw(check_markup($signature_text, $this->filtered_html_format->id()), 'Filtered signature text found.');
     // Verify that the user signature's text format's cache tag is present.
     $this->drupalGet('node/' . $node->id());
-    $this->assertTrue(in_array('filter_format:filtered_html_format', explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'))));
+    $this->assertTrue(in_array('config:filter.format.filtered_html_format', explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'))));
 
     // Verify the signature field is available on Manage form display page.
     \Drupal::config('user.settings')->set('signatures', 0)->save();
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index a02d098..c23ab68 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -535,6 +535,7 @@ function template_preprocess_username(&$variables) {
   $variables['extra'] = '';
   $variables['uid'] = $account->id();
   if (empty($variables['uid'])) {
+    theme_add_setting_cache_tag();
     if (theme_get_setting('features.comment_user_verification')) {
       $variables['extra'] = ' (' . t('not verified') . ')';
     }
diff --git a/core/modules/views/src/Plugin/ViewsHandlerManager.php b/core/modules/views/src/Plugin/ViewsHandlerManager.php
index ac7abda..9d3fda4 100644
--- a/core/modules/views/src/Plugin/ViewsHandlerManager.php
+++ b/core/modules/views/src/Plugin/ViewsHandlerManager.php
@@ -59,7 +59,7 @@ public function __construct($handler_type, \Traversable $namespaces, ViewsData $
     }
     parent::__construct("Plugin/views/$handler_type", $namespaces, $module_handler, $plugin_interface, $plugin_definition_annotation_name);
 
-    $this->setCacheBackend($cache_backend, "views:$handler_type", array('extension', 'extension:views'));
+    $this->setCacheBackend($cache_backend, "views:$handler_type", array('config:core.extension', 'extension:views'));
     $this->alterInfo('views_plugins_' . $handler_type);
 
     $this->viewsData = $views_data;
diff --git a/core/modules/views/src/Plugin/ViewsPluginManager.php b/core/modules/views/src/Plugin/ViewsPluginManager.php
index c340ca2..8e83d42 100644
--- a/core/modules/views/src/Plugin/ViewsPluginManager.php
+++ b/core/modules/views/src/Plugin/ViewsPluginManager.php
@@ -43,7 +43,7 @@ public function __construct($type, \Traversable $namespaces, CacheBackendInterfa
     );
 
     $this->alterInfo('views_plugins_' . $type);
-    $this->setCacheBackend($cache_backend, "views:{$type}_plugins", array('extension', 'extension:views'));
+    $this->setCacheBackend($cache_backend, "views:{$type}_plugins", array('config:core.extension', 'extension:views'));
   }
 
 }
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index 1369b8a..fe3909c 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -203,8 +203,7 @@ public function cacheGet($type) {
    * to be sure that we catch everything. Maybe that's a bad idea.
    */
   public function cacheFlush() {
-    $id = $this->view->storage->id();
-    Cache::invalidateTags(array('view:' . $id));
+    Cache::invalidateTags($this->view->storage->getCacheTags());
   }
 
   /**
@@ -321,12 +320,11 @@ public function generateOutputKey() {
   /**
    * Gets an array of cache tags for the current view.
    *
-   * @return array
-   *   An array fo cache tags based on the current view.
+   * @return string[]
+   *   An array of cache tags based on the current view.
    */
   protected function getCacheTags() {
-    $id = $this->view->storage->id();
-    $tags = array('view:' . $id);
+    $tags = $this->view->storage->getCacheTags();
 
     $entity_information = $this->view->query->getEntityTableInfo();
 
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 13ff168..4f1cb1c 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -202,8 +202,7 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
         }
         else {
           $this->unpackOptions($this->options, $options);
-          $id = $this->view->storage->id();
-          \Drupal::cache('data')->set($cid, $this->options, Cache::PERMANENT, array('extension', 'extension:views', 'view:' . $id));
+          \Drupal::cache('data')->set($cid, $this->options, Cache::PERMANENT, Cache::mergeTags(array('config:core.extension', 'extension:views'), $this->view->storage->getCacheTags()));
         }
         static::$unpackOptions[$cid] = $this->options;
       }
diff --git a/core/modules/views/src/ViewsData.php b/core/modules/views/src/ViewsData.php
index 796e94b..9ac3896 100644
--- a/core/modules/views/src/ViewsData.php
+++ b/core/modules/views/src/ViewsData.php
@@ -200,7 +200,7 @@ protected function cacheGet($cid) {
    *   The data that will be cached.
    */
   protected function cacheSet($cid, $data) {
-    return $this->cacheBackend->set($this->prepareCid($cid), $data, Cache::PERMANENT, array('views_data', 'extension', 'extension:views'));
+    return $this->cacheBackend->set($this->prepareCid($cid), $data, Cache::PERMANENT, array('views_data', 'config:core.extension', 'extension:views'));
   }
 
   /**
diff --git a/core/modules/views/views.module b/core/modules/views/views.module
index 7c170dc..d7d5ec4 100644
--- a/core/modules/views/views.module
+++ b/core/modules/views/views.module
@@ -472,7 +472,7 @@ function views_field_config_create(FieldConfigInterface $field) {
  * Implements hook_ENTITY_TYPE_update() for 'field_config'.
  */
 function views_field_config_update(FieldConfigInterface $field) {
-  Cache::deleteTags(array('extension' => 'views'));
+  Cache::deleteTags(['extension:views']);
   \Drupal::cache('render')->deleteAll();
 }
 
@@ -480,7 +480,7 @@ function views_field_config_update(FieldConfigInterface $field) {
  * Implements hook_ENTITY_TYPE_delete() for 'field_config'.
  */
 function views_field_config_delete(FieldConfigInterface $field) {
-  Cache::deleteTags(array('extension' => 'views'));
+  Cache::deleteTags(['extension:views']);
   \Drupal::cache('render')->deleteAll();
 }
 
@@ -489,7 +489,7 @@ function views_field_config_delete(FieldConfigInterface $field) {
  */
 function views_invalidate_cache() {
   // Clear Views' info cache entries.
-  Cache::deleteTags(array('extension' => 'views'));
+  Cache::deleteTags(['extension:views']);
 
   // Set the menu as needed to be rebuilt.
   \Drupal::service('router.builder_indicator')->setRebuildNeeded();
diff --git a/core/modules/views_ui/src/ViewUI.php b/core/modules/views_ui/src/ViewUI.php
index 4a08788..893d726 100644
--- a/core/modules/views_ui/src/ViewUI.php
+++ b/core/modules/views_ui/src/ViewUI.php
@@ -1150,7 +1150,7 @@ public function getDependencies() {
    * {@inheritdoc}
    */
   public function getCacheTags() {
-    $this->storage->getCacheTags();
+    return $this->storage->getCacheTags();
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
index 98cffaf..ec1cdd6 100644
--- a/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Access/AccessManagerTest.php
@@ -555,7 +555,7 @@ public function testCheckNamedRouteWithNonExistingRoute() {
     $this->setupAccessChecker();
 
     $this->assertEquals(FALSE, $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account), 'A non existing route lead to access.');
-    $this->assertEquals(AccessResult::forbidden()->addCacheTags(array('extension')), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE), 'A non existing route lead to access.');
+    $this->assertEquals(AccessResult::forbidden()->addCacheTags(['config:core.extension']), $this->accessManager->checkNamedRoute('test_route_1', array(), $this->account, TRUE), 'A non existing route lead to access.');
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
index 1213614..4c3bc64 100644
--- a/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
+++ b/core/tests/Drupal/Tests/Core/Access/AccessResultTest.php
@@ -400,6 +400,7 @@ public function testCacheContexts() {
    * @covers ::resetCacheTags
    * @covers ::getCacheTags
    * @covers ::cacheUntilEntityChanges
+   * @covers ::cacheUntilConfigurationChanges
    */
   public function testCacheTags() {
     $verify = function (AccessResult $access, array $tags) {
@@ -447,6 +448,18 @@ public function testCacheTags() {
     $b = AccessResult::neutral()->cacheUntilEntityChanges($node);
     $verify($b, $tags);
     $this->assertEquals($a, $b);
+
+    // ::cacheUntilEntityChanges() convenience method.
+    $configuration = $this->getMock('\Drupal\Core\Config\ConfigBase');
+    $configuration->expects($this->any())
+      ->method('getCacheTags')
+      ->will($this->returnValue(array('config:system.theme.global')));
+    $tags = array('config:system.theme.global');
+    $a = AccessResult::neutral()->addCacheTags($tags);
+    $verify($a, $tags);
+    $b = AccessResult::neutral()->cacheUntilConfigurationChanges($configuration);
+    $verify($b, $tags);
+    $this->assertEquals($a, $b);
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/Config/ConfigTest.php b/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
index 4111e08..215b915 100644
--- a/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/ConfigTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Config;
 
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Tests\UnitTestCase;
 use Drupal\Core\Config\Config;
 use Drupal\Component\Utility\String;
@@ -55,6 +56,10 @@ public function setUp() {
     $this->eventDispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
     $this->typedConfig = $this->getMock('\Drupal\Core\Config\TypedConfigManagerInterface');
     $this->config = new Config('config.test', $this->storage, $this->eventDispatcher, $this->typedConfig);
+
+    $container = new ContainerBuilder();
+    $container->setParameter('cache_bins', []);
+    \Drupal::setContainer($container);
   }
 
   /**
@@ -118,6 +123,15 @@ public function testSetData($data) {
    * @dataProvider nestedDataProvider
    */
   public function testSave($data) {
+    // Mock the cache backend, verifying that invalidateTags() is invoked on it.
+    $cache_backend = $this->getMock('\Drupal\Core\Cache\CacheBackendInterface');
+    $cache_backend->expects($this->once())
+      ->method('invalidateTags')
+      ->with(['config:config.test']);
+    $container = \Drupal::getContainer();
+    $container->set('cache.mocked', $cache_backend);
+    $container->setParameter('cache_bins', ['cache.mocked' => 'mocked']);
+
     // Set initial data.
     $this->config->setData($data);
 
@@ -356,6 +370,13 @@ public function testValidateNameException($name, $exception_message) {
   }
 
   /**
+   * @covers ::getCacheTags
+   */
+  public function testGetCacheTags() {
+    $this->assertSame(['config:' . $this->config->getName()], $this->config->getCacheTags());
+  }
+
+  /**
    * Provides data to test name validation.
    *
    * @see \Drupal\Tests\Core\Config\ConfigTest::testValidateNameException()
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
index 2da84fe..e5f6844 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityBaseUnitTest.php
@@ -106,6 +106,9 @@ protected function setUp() {
     $this->entityType->expects($this->any())
       ->method('getProvider')
       ->will($this->returnValue($this->provider));
+    $this->entityType->expects($this->any())
+      ->method('getConfigPrefix')
+      ->willReturn('test_provider.' . $this->entityTypeId);
 
     $this->entityManager = $this->getMock('\Drupal\Core\Entity\EntityManagerInterface');
     $this->entityManager->expects($this->any())
@@ -363,7 +366,7 @@ public function testEnable() {
   public function testDisable() {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
-      ->with(array($this->entityTypeId . ':' . $this->id));
+      ->with(array('config:test_provider.'  . $this->entityTypeId . '.' . $this->id));
 
     $this->entity->setStatus(TRUE);
     $this->assertSame($this->entity, $this->entity->disable());
diff --git a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
index ddfbd81..3db588c 100644
--- a/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/Entity/ConfigEntityStorageTest.php
@@ -302,7 +302,7 @@ public function testSaveUpdate(EntityInterface $entity) {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . ':foo', // Own cache tag.
+        'config:the_config_prefix.foo', // Own cache tag.
         $this->entityTypeId . '_list', // List cache tag.
       ));
 
@@ -362,7 +362,7 @@ public function testSaveRename(ConfigEntityInterface $entity) {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId .':bar', // Own cache tag.
+        'config:the_config_prefix.bar', // Own cache tag.
         $this->entityTypeId . '_list', // List cache tag.
       ));
 
@@ -728,8 +728,8 @@ public function testDelete() {
     $this->cacheBackend->expects($this->once())
       ->method('invalidateTags')
       ->with(array(
-        $this->entityTypeId . ':bar', // Own cache tag.
-        $this->entityTypeId . ':foo', // Own cache tag.
+        'config:the_config_prefix.bar', // Own cache tag.
+        'config:the_config_prefix.foo', // Own cache tag.
         $this->entityTypeId . '_list', // List cache tag.
       ));
 
diff --git a/core/themes/bartik/bartik.theme b/core/themes/bartik/bartik.theme
index 4d6ef3a..6700652 100644
--- a/core/themes/bartik/bartik.theme
+++ b/core/themes/bartik/bartik.theme
@@ -143,6 +143,7 @@ function _bartik_process_page(&$variables) {
   $site_config = \Drupal::config('system.site');
   // Always print the site name and slogan, but if they are toggled off, we'll
   // just hide them visually.
+  theme_add_setting_cache_tag();
   $variables['hide_site_name']   = theme_get_setting('features.name') ? FALSE : TRUE;
   $variables['hide_site_slogan'] = theme_get_setting('features.slogan') ? FALSE : TRUE;
   if ($variables['hide_site_name']) {
