diff --git a/core/modules/node/src/Tests/Views/FrontPageTest.php b/core/modules/node/src/Tests/Views/FrontPageTest.php
index eb970d6..e4c9f55 100644
--- a/core/modules/node/src/Tests/Views/FrontPageTest.php
+++ b/core/modules/node/src/Tests/Views/FrontPageTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\node\Tests\Views;
 
+use Drupal\system\Tests\Cache\AssertPageCacheTagsTrait;
 use Drupal\views\Tests\ViewTestBase;
 use Drupal\views\ViewExecutable;
 use Drupal\views\Views;
@@ -18,6 +19,13 @@
  */
 class FrontPageTest extends ViewTestBase {
 
+  use AssertPageCacheTagsTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $dumpHeaders = TRUE;
+
   /**
    * The entity storage for nodes.
    *
@@ -173,4 +181,45 @@ public function testAdminFrontPage() {
     $this->assertPattern('/class=".+view-frontpage/', 'Frontpage view was rendered');
   }
 
+  /**
+   * Tests the cache tags on the front page.
+   */
+  public function testCacheTags() {
+    $this->enablePageCaching();
+
+    // Create some nodes on the frontpage view. Add more than 10 nodes in order
+    // to enable paging.
+    $this->drupalCreateContentType(['type' => 'article']);
+    for ($i = 0; $i < 15; $i++) {
+      $this->drupalCreateNode(['type' => 'article', 'created' => $i]);
+    }
+
+    // First page.
+    $this->assertPageCacheTags('node', [], [
+      'config:filter.format.plain_text',
+      'config:views.view.frontpage',
+      'node_list',
+      'node_view',
+      'node:6', 'node:7', 'node:8', 'node:9', 'node:10',
+      'node:11', 'node:12', 'node:13', 'node:14', 'node:15',
+      'user_view',
+      'user:0',
+      'rendered',
+    ]);
+
+    // Second page.
+    $this->assertPageCacheTags('node', ['query' => ['page' => 1]], [
+      // The cache tags for the listed nodes.
+      'node:1', 'node:2', 'node:3', 'node:4', 'node:5',
+      // The rest.
+      'config:filter.format.plain_text',
+      'config:views.view.frontpage',
+      'node_list',
+      'node_view',
+      'user_view',
+      'user:0',
+      'rendered',
+    ]);
+  }
+
 }
diff --git a/core/modules/system/src/Tests/Cache/AssertPageCacheTagsTrait.php b/core/modules/system/src/Tests/Cache/AssertPageCacheTagsTrait.php
new file mode 100644
index 0000000..39a31ac
--- /dev/null
+++ b/core/modules/system/src/Tests/Cache/AssertPageCacheTagsTrait.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Cache\AssertPageCacheTagsTrait.
+ */
+
+namespace Drupal\system\Tests\Cache;
+
+/**
+ * Provides test assertions for testing page-level cache tags.
+ *
+ * Can be used by test classes that extend \Drupal\simpletest\WebTestBase.
+ */
+trait AssertPageCacheTagsTrait {
+
+
+  /**
+   * Enables page caching.
+   */
+  protected function enablePageCaching() {
+    $config = $this->config('system.performance');
+    $config->set('cache.page.use_internal', 1);
+    $config->set('cache.page.max_age', 300);
+    $config->save();
+  }
+
+  /**
+   * Fills page cache for the given path, asserts cache tags on page cache hit.
+   *
+   * @param string $path
+   *   The Drupal page path to test. See WebTestBase::drupalGet().
+   * @param array $options
+   *   Options to be forwarded to URL generator. See WebTestBase::drupalGet().
+   * @param string[] $expected_tags
+   *   The expected cache tags for the page cache entry of the given $path.
+   */
+  protected function assertPageCacheTags($path, array $options = [], array $expected_tags) {
+    sort($expected_tags);
+    $this->drupalGet($path, $options);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+    $actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
+    sort($actual_tags);
+    $this->assertIdentical($actual_tags, $expected_tags);
+    $this->drupalGet($path, $options);
+    $actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
+    sort($actual_tags);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
+    $this->assertIdentical($actual_tags, $expected_tags);
+    $options['absolute'] = TRUE;
+    $cid_parts = array(_url($path, $options), 'html');
+    $cid = implode(':', $cid_parts);
+    $cache_entry = \Drupal::cache('render')->get($cid);
+    sort($cache_entry->tags);
+    $this->assertEqual($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 7ce1b5b..8e47551 100644
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
@@ -7,9 +7,7 @@
 
 namespace Drupal\system\Tests\Cache;
 
-use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
-use Drupal\Core\Cache\Cache;
 
 /**
  * Enables the page cache and tests its cache tags in various scenarios.
@@ -21,6 +19,8 @@
  */
 class PageCacheTagsIntegrationTest extends WebTestBase {
 
+  use AssertPageCacheTagsTrait;
+
   protected $profile = 'standard';
 
   protected $dumpHeaders = TRUE;
@@ -31,10 +31,7 @@ class PageCacheTagsIntegrationTest extends WebTestBase {
   protected function setUp() {
     parent::setUp();
 
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->save();
+    $this->enablePageCaching();
   }
 
   /**
@@ -71,7 +68,7 @@ function testPageCacheTags() {
     ));
 
     // Full node page 1.
-    $this->verifyPageCacheTags($node_1->urlInfo(), array(
+    $this->assertPageCacheTags($node_1->urlInfo(), [], array(
       'rendered',
       'block_view',
       'config:block_list',
@@ -102,7 +99,7 @@ function testPageCacheTags() {
     ));
 
     // Full node page 2.
-    $this->verifyPageCacheTags($node_2->urlInfo(), array(
+    $this->assertPageCacheTags($node_2->urlInfo(), [], array(
       'rendered',
       'block_view',
       'config:block_list',
@@ -116,6 +113,7 @@ function testPageCacheTags() {
       'config:block.block.bartik_main_menu',
       'config:block.block.bartik_account_menu',
       'block_plugin:system_breadcrumb_block',
+      'config:views.view.comments_recent',
       'block_plugin:system_main_block',
       'block_plugin:system_menu_block__account',
       'block_plugin:system_menu_block__main',
@@ -132,36 +130,9 @@ function testPageCacheTags() {
       'config:system.menu.tools',
       'config:system.menu.footer',
       'config:system.menu.main',
+      'comment_list',
+      'node_list',
     ));
   }
 
-  /**
-   * Fills page cache for the given path, verify cache tags on page cache hit.
-   *
-   * @param \Drupal\Core\Url $url
-   *   The url
-   * @param $expected_tags
-   *   The expected cache tags for the page cache entry of the given $path.
-   */
-  protected function verifyPageCacheTags(Url $url, $expected_tags) {
-    // @todo Change ->drupalGet() calls to just pass $url when
-    //   https://www.drupal.org/node/2350837 gets committed
-    sort($expected_tags);
-    $this->drupalGet($url->setAbsolute()->toString());
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-    $actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    sort($actual_tags);
-    $this->assertIdentical($actual_tags, $expected_tags);
-    $this->drupalGet($url->setAbsolute()->toString());
-    $actual_tags = explode(' ', $this->drupalGetHeader('X-Drupal-Cache-Tags'));
-    sort($actual_tags);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
-    $this->assertIdentical($actual_tags, $expected_tags);
-    $cid_parts = array($url->setAbsolute()->toString(), 'html');
-    $cid = implode(':', $cid_parts);
-    $cache_entry = \Drupal::cache('render')->get($cid);
-    sort($cache_entry->tags);
-    $this->assertEqual($cache_entry->tags, $expected_tags);
-  }
-
 }
diff --git a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
index 56c0fbb..5ddd4d4 100644
--- a/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
+++ b/core/modules/views/src/Plugin/views/cache/CachePluginBase.php
@@ -178,12 +178,12 @@ public function cacheSet($type) {
           'total_rows' => isset($this->view->total_rows) ? $this->view->total_rows : 0,
           'current_page' => $this->view->getCurrentPage(),
         );
-        \Drupal::cache($this->resultsBin)->set($this->generateResultsKey(), $data, $this->cacheSetExpire($type), $this->getCacheTags());
+        \Drupal::cache($this->resultsBin)->set($this->generateResultsKey(), $data, $this->cacheSetExpire($type), $this->view->getCacheTags());
         break;
       case 'output':
         $this->renderer->render($this->view->display_handler->output);
         $this->storage = $this->renderer->getCacheableRenderArray($this->view->display_handler->output);
-        \Drupal::cache($this->outputBin)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), $this->getCacheTags());
+        \Drupal::cache($this->outputBin)->set($this->generateOutputKey(), $this->storage, $this->cacheSetExpire($type), $this->view->getCacheTags());
         break;
     }
   }
@@ -231,9 +231,6 @@ public function cacheGet($type) {
 
   /**
    * Clear out cached data for a view.
-   *
-   * We're just going to nuke anything related to the view, regardless of display,
-   * to be sure that we catch everything. Maybe that's a bad idea.
    */
   public function cacheFlush() {
     Cache::invalidateTags($this->view->storage->getCacheTags());
@@ -335,15 +332,15 @@ public function generateOutputKey() {
    * @return string[]
    *   An array of cache tags based on the current view.
    */
-  protected function getCacheTags() {
+  public function getCacheTags() {
     $tags = $this->view->storage->getCacheTags();
 
+    // The list cache tags for the entity types listed in this view.
     $entity_information = $this->view->query->getEntityTableInfo();
-
     if (!empty($entity_information)) {
       // Add the list cache tags for each entity type used by this view.
-      foreach (array_keys($entity_information) as $entity_type) {
-        $tags = Cache::mergeTags($tags, \Drupal::entityManager()->getDefinition($entity_type)->getListCacheTags());
+      foreach ($entity_information as $table => $metadata) {
+        $tags = Cache::mergeTags($tags, \Drupal::entityManager()->getDefinition($metadata['entity_type'])->getListCacheTags());
       }
     }
 
diff --git a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
index 93bd846..5c83188 100644
--- a/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/src/Plugin/views/display/DisplayPluginBase.php
@@ -2050,6 +2050,10 @@ public function render() {
       '#post_render_cache' => &$this->view->element['#post_render_cache'],
     );
 
+    // If the output is a render array, add cache tags, regardless of whether
+    // caching is enabled or not; cache tags must always be set.
+    $element['#cache']['tags'] = $this->view->getCacheTags();
+
     return $element;
   }
 
diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php
index c9cb84a..49704e4 100644
--- a/core/modules/views/src/Tests/GlossaryTest.php
+++ b/core/modules/views/src/Tests/GlossaryTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Url;
+use Drupal\system\Tests\Cache\AssertPageCacheTagsTrait;
 use Drupal\views\Views;
 
 /**
@@ -18,6 +19,13 @@
  */
 class GlossaryTest extends ViewTestBase {
 
+  use AssertPageCacheTagsTrait;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $dumpHeaders = TRUE;
+
   /**
    * Modules to enable.
    *
@@ -77,6 +85,16 @@ public function testGlossaryView() {
       $result_count = trim(str_replace(array('|', '(', ')'), '', (string) $result[0]));
       $this->assertEqual($result_count, $count, 'The expected number got rendered.');
     }
+
+    // Verify cache tags.
+    $this->enablePageCaching();
+    $this->assertPageCacheTags('glossary', [], [
+      'config:views.view.glossary',
+      'node_list',
+      // @todo Make sure the cache tags for the individual nodes also exist.
+      'user_list',
+      'rendered',
+    ]);
   }
 
 }
diff --git a/core/modules/views/src/Tests/Plugin/CacheTest.php b/core/modules/views/src/Tests/Plugin/CacheTest.php
index 68f50f7..f9472e4 100644
--- a/core/modules/views/src/Tests/Plugin/CacheTest.php
+++ b/core/modules/views/src/Tests/Plugin/CacheTest.php
@@ -149,7 +149,7 @@ function testHeaderStorage() {
     drupal_render($output);
     $this->assertTrue(in_array('views_test_data/test', $output['#attached']['library']), 'Make sure libraries are added for cached views.');
     $this->assertEqual(['foo' => 'bar'], $output['#attached']['drupalSettings'], 'Make sure drupalSettings are added for cached views.');
-    $this->assertEqual(['views_test_data:1'], $output['#cache']['tags']);
+    $this->assertEqual(['config:views.view.test_cache_header_storage'], $output['#cache']['tags']);
     $this->assertEqual(['views_test_data_post_render_cache' => [['foo' => 'bar']]], $output['#post_render_cache']);
     $this->assertFalse(!empty($view->build_info['pre_render_called']), 'Make sure hook_views_pre_render is not called for the cached view.');
   }
diff --git a/core/modules/views/src/ViewExecutable.php b/core/modules/views/src/ViewExecutable.php
index db01ad8..ee02153 100644
--- a/core/modules/views/src/ViewExecutable.php
+++ b/core/modules/views/src/ViewExecutable.php
@@ -8,6 +8,7 @@
 namespace Drupal\views;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Routing\RouteProviderInterface;
@@ -1387,6 +1388,7 @@ public function render($display_id = NULL) {
       }
 
       $this->display_handler->output = $this->display_handler->render();
+
       if ($cache) {
         $cache->cacheSet('output');
       }
@@ -1413,6 +1415,21 @@ public function render($display_id = NULL) {
   }
 
   /**
+   * Gets the cache tags associated with the executed view.
+   *
+   * Returns the view's cache tag plus the listed entity types' list cache tags.
+   *
+   * @return string[]
+   *   An array of cache tags.
+   */
+  public function getCacheTags() {
+    $this->initDisplay();
+    /** @var \Drupal\views\Plugin\views\cache\CachePluginBase $cache */
+    $cache = $this->display_handler->getPlugin('cache');
+    return $cache->getCacheTags();
+  }
+
+  /**
    * Builds the render array outline for the given display.
    *
    * This render array has a #pre_render callback which will call
