diff --git a/core/modules/views/config/schema/views.data_types.schema.yml b/core/modules/views/config/schema/views.data_types.schema.yml
index 8ebcea0..da16f2c 100644
--- a/core/modules/views/config/schema/views.data_types.schema.yml
+++ b/core/modules/views/config/schema/views.data_types.schema.yml
@@ -268,6 +268,15 @@ views_display:
     field_langcode_add_to_query:
       type: string
       label: 'Add the field language to the query'
+    cache_information:
+      type: mapping
+      mapping:
+        cacheable:
+          type: boolean
+          label: "Cacheable"
+        contexts:
+          type: sequence
+          label: Cache contexts"
 
 views_sort:
   type: views_handler
diff --git a/core/modules/views/src/Entity/View.php b/core/modules/views/src/Entity/View.php
index 5bb0a94..81ec8ae 100644
--- a/core/modules/views/src/Entity/View.php
+++ b/core/modules/views/src/Entity/View.php
@@ -298,6 +298,37 @@ public function calculateDependencies() {
   /**
    * {@inheritdoc}
    */
+  public function preSave(EntityStorageInterface $storage) {
+    parent::preSave($storage);
+
+    // @todo Check whether isSyncing is needed.
+    if (!$this->isSyncing()) {
+      $this->fillCacheInformation();
+    }
+  }
+
+  /**
+   * Fill in all the cache information (cache contexts as cacheablity).
+   *
+   * The cache information is stored per view and per display.
+   */
+  protected function fillCacheInformation() {
+    $executable = $this->getExecutable();
+
+    $displays = $this->get('display');
+    foreach ($displays as $display_id => $display) {
+      $executable->setDisplay($display_id);
+
+      list($display['cache_information']['contexts'], $display['cache_information']['cacheable']) = $executable->getDisplay()->getCacheInformation();
+      // Always include at least the language context.
+      $display['cache_information']['contexts'][] = 'cache.context.language';
+      $display['cache_information']['contexts'] = array_unique($display['cache']['contexts']);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
     parent::postSave($storage, $update);
 
diff --git a/core/modules/views/src/Plugin/CacheablePluginInterface.php b/core/modules/views/src/Plugin/CacheablePluginInterface.php
new file mode 100644
index 0000000..847af59
--- /dev/null
+++ b/core/modules/views/src/Plugin/CacheablePluginInterface.php
@@ -0,0 +1,22 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\views\Plugin\CacheablePluginInterface.
+ */
+
+namespace Drupal\views\Plugin;
+
+interface CacheablePluginInterface {
+
+  /**
+   * @return bool
+   */
+  public function isCacheable();
+
+  /**
+   * @return string[]
+   */
+  public function getCacheContexts();
+
+}
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 31f30bc..5a63379 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Theme\Registry;
+use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\area\AreaPluginBase;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Plugin\views\PluginBase;
@@ -2213,6 +2214,45 @@ public function preExecute() {
   }
 
   /**
+   * Determines the available cache information from each handler/plugin.
+   *
+   * @return array
+   *   Returns an array:
+   *     - first value: (boolean) Is the display actually cacheable.
+   *     - second value: An array of cache contexts the view output varies by.
+   */
+  public function getCacheInformation () {
+    $cacheable = TRUE;
+    $cache_contexts = [];
+
+    // Iterate over ordinary views plugins.
+    foreach (Views::getPluginTypes('plugin') as $plugin_type) {
+      $plugin = $this->getPlugin($plugin_type);
+      if ($plugin instanceof CacheablePluginInterface) {
+        $cache_contexts = array_merge($cache_contexts, $plugin->getCacheContexts());
+        $cacheable &= $plugin->isCacheable();
+      }
+      else {
+        $cacheable = FALSE;
+      }
+    }
+
+    // Iterate over all handles. Note that at least the argument handler will
+    // need to ask all its subplugins.
+    foreach (array_keys(Views::getHandlerTypes()) as $handler_type) {
+      $handlers = $this->getHandlers($handler_type);
+      foreach ($handlers as $handler) {
+        if ($handler instanceof CacheablePluginInterface) {
+          $cache_contexts = array_merge($cache_contexts, $handler->getCacheContexts());
+          $cacheable &= $handler->isCacheable();
+        }
+      }
+    }
+
+    return [$cache_contexts, $cacheable];
+  }
+
+  /**
    * When used externally, this is how a view gets run and returns
    * data in the format required.
    *
diff --git a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
index 3f5ecb9..291fc4e 100644
--- a/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
+++ b/core/modules/views/src/Plugin/views/filter/FilterPluginBase.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\views\Plugin\CacheablePluginInterface;
 use Drupal\views\Plugin\views\HandlerBase;
 use Drupal\Component\Utility\String as UtilityString;
 use Drupal\views\Plugin\views\display\DisplayPluginBase;
@@ -43,7 +44,7 @@
 /**
  * Base class for Views filters handler plugins.
  */
-abstract class FilterPluginBase extends HandlerBase {
+abstract class FilterPluginBase extends HandlerBase implements CacheablePluginInterface {
 
   /**
    * Contains the actual value of the field,either configured in the views ui
@@ -1457,6 +1458,25 @@ protected static function arrayFilterZero($var) {
     return trim($var) != '';
   }
 
+  /**
+   * @return string[]
+   */
+  public function getCacheContexts() {
+    $cache_contexts = [];
+    if ($this->isExposed()) {
+      $cache_contexts[] = 'cache.context.url';
+    }
+    return $cache_contexts;
+  }
+
+  /**
+   * @return bool
+   */
+  public function isCacheable() {
+    return !$this->isExposed();
+  }
+
+
 }
 
 /**
