diff --git a/core/modules/block/tests/modules/block_test_views/test_views/views.view.test_view_block.yml b/core/modules/block/tests/modules/block_test_views/test_views/views.view.test_view_block.yml
index e16db6b..4fb4cc1 100644
--- a/core/modules/block/tests/modules/block_test_views/test_views/views.view.test_view_block.yml
+++ b/core/modules/block/tests/modules/block_test_views/test_views/views.view.test_view_block.yml
@@ -11,7 +11,7 @@ display:
     position: null
     display_options:
       access:
-        type: perm
+        type: none
       cache:
         type: none
       query:
diff --git a/core/modules/entity_reference/src/Tests/Views/EntityReferenceRelationshipTest.php b/core/modules/entity_reference/src/Tests/Views/EntityReferenceRelationshipTest.php
index 3f9d7b1..9c3c2ab 100644
--- a/core/modules/entity_reference/src/Tests/Views/EntityReferenceRelationshipTest.php
+++ b/core/modules/entity_reference/src/Tests/Views/EntityReferenceRelationshipTest.php
@@ -90,6 +90,8 @@ protected function setUp() {
     $entity->field_test->target_id = $referenced_entity->id();
     $entity->save();
     $this->entities[$entity->id()] = $entity;
+
+    Views::viewsData()->clear();
   }
 
   /**
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..5ae6fd3 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_information']['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();
+  }
+
+
 }
 
 /**
diff --git a/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php b/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php
index e019168..8016685 100644
--- a/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php
+++ b/core/modules/views/src/Tests/Plugin/RelationshipJoinTestBase.php
@@ -6,6 +6,7 @@
  */
 
 namespace Drupal\views\Tests\Plugin;
+use Drupal\views\Views;
 
 /**
  * Provides a base class for a testing a relationship.
@@ -39,6 +40,8 @@ protected function setUpFixtures() {
     $this->installSchema('system', 'sequences');
     $this->root_user = entity_create('user', array('name' => $this->randomMachineName()));
     $this->root_user->save();
+
+    Views::viewsData()->clear();
   }
 
   /**
diff --git a/core/modules/views/src/Tests/Plugin/RowEntityTest.php b/core/modules/views/src/Tests/Plugin/RowEntityTest.php
index 583fb61..abdb06d 100644
--- a/core/modules/views/src/Tests/Plugin/RowEntityTest.php
+++ b/core/modules/views/src/Tests/Plugin/RowEntityTest.php
@@ -24,7 +24,7 @@ class RowEntityTest extends ViewUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('taxonomy', 'text', 'filter', 'field', 'entity', 'system');
+  public static $modules = array('taxonomy', 'text', 'filter', 'field', 'entity', 'system', 'node', 'user');
 
   /**
    * Views used by this test.
diff --git a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml
index df3bbe2..164abad 100644
--- a/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml
+++ b/core/modules/views/tests/modules/views_test_config/test_views/views.view.test_view.yml
@@ -10,6 +10,8 @@ display:
         pager: '0'
         pager_options: '0'
         sorts: '0'
+      row:
+        type: fields
       fields:
         age:
           field: age
