 core/includes/common.inc                           |   19 +----------
 core/lib/Drupal/Core/Cache/Cache.php               |   28 +++++++++++++++
 core/lib/Drupal/Core/Cache/DatabaseBackend.php     |   36 +++-----------------
 core/lib/Drupal/Core/Cache/MemoryBackend.php       |   34 ++----------------
 .../Core/EventSubscriber/HtmlViewSubscriber.php    |    2 +-
 .../Core/Page/DefaultHtmlFragmentRenderer.php      |    4 +++
 .../system/Tests/Bootstrap/PageCacheTest.php       |    1 +
 .../Tests/Cache/PageCacheTagsIntegrationTest.php   |   10 +++++-
 .../system/Tests/Cache/PageCacheTagsTestBase.php   |    2 ++
 9 files changed, 53 insertions(+), 83 deletions(-)

diff --git a/core/includes/common.inc b/core/includes/common.inc
index af96028..17a9f78 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -3241,7 +3241,7 @@ function drupal_page_set_cache(Response $response, Request $request) {
         // because by the time it is read, the configuration might change.
         'page_compressed' => $page_compressed,
       ),
-      'tags' => array('content' => TRUE) + drupal_cache_tags_page_get($response),
+      'tags' => explode(' ', $response->headers->get('X-Drupal-Cache-Tags')),
       'expire' => Cache::PERMANENT,
       'created' => REQUEST_TIME,
     );
@@ -4484,23 +4484,6 @@ function drupal_render_collect_cache_tags($element, $tags = array()) {
 }
 
 /**
- * Return the cache tags that were stored during drupal_render_page().
- *
- * @param \Symfony\Component\HttpFoundation\Response $response
- *   The response object.
- * @return array
- *   An array of cache tags.
- *
- * @see \Drupal\Core\EventSubscriber\HtmlViewSubscriber::onHtmlPage()
- */
-function drupal_cache_tags_page_get(Response $response) {
-  if (($tags = $response->headers->get('cache_tags')) && $tags = unserialize($tags)) {
-    return $tags;
-  }
-  return array();
-}
-
-/**
  * Prepares an element for caching based on a query.
  *
  * This smart caching strategy saves Drupal from querying and rendering to HTML
diff --git a/core/lib/Drupal/Core/Cache/Cache.php b/core/lib/Drupal/Core/Cache/Cache.php
index c2f5fe6..feb3c77 100644
--- a/core/lib/Drupal/Core/Cache/Cache.php
+++ b/core/lib/Drupal/Core/Cache/Cache.php
@@ -70,4 +70,32 @@ public static function getBins() {
     return $bins;
   }
 
+  /**
+   * 'Flattens' a tags array into an array of strings.
+   *
+   * @param array $tags
+   *   Associative array of tags to flatten.
+   *
+   * @return array
+   *   An indexed array of flattened tag identifiers.
+   */
+  public static function flattenTags(array $tags) {
+    if (isset($tags[0])) {
+      return $tags;
+    }
+
+    $flat_tags = array();
+    foreach ($tags as $namespace => $values) {
+      if (is_array($values)) {
+        foreach ($values as $value) {
+          $flat_tags[] = "$namespace:$value";
+        }
+      }
+      else {
+        $flat_tags[] = "$namespace:$values";
+      }
+    }
+    return $flat_tags;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Cache/DatabaseBackend.php b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
index 4f98c50..ad873e6 100644
--- a/core/lib/Drupal/Core/Cache/DatabaseBackend.php
+++ b/core/lib/Drupal/Core/Cache/DatabaseBackend.php
@@ -163,7 +163,7 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array
    * Actually set the cache.
    */
   protected function doSet($cid, $data, $expire, $tags) {
-    $flat_tags = $this->flattenTags($tags);
+    $flat_tags = Cache::flattenTags($tags);
     $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
     $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
     // Remove tags that were already deleted or invalidated during this request
@@ -237,7 +237,7 @@ public function deleteMultiple(array $cids) {
   public function deleteTags(array $tags) {
     $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
     $deleted_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::deletedTags', array());
-    foreach ($this->flattenTags($tags) as $tag) {
+    foreach (Cache::flattenTags($tags) as $tag) {
       // Only delete tags once per request unless they are written again.
       if (isset($deleted_tags[$tag])) {
         continue;
@@ -307,7 +307,7 @@ public function invalidateTags(array $tags) {
     try {
       $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
       $invalidated_tags = &drupal_static('Drupal\Core\Cache\DatabaseBackend::invalidatedTags', array());
-      foreach ($this->flattenTags($tags) as $tag) {
+      foreach (Cache::flattenTags($tags) as $tag) {
         // Only invalidate tags once per request unless they are written again.
         if (isset($invalidated_tags[$tag])) {
           continue;
@@ -358,34 +358,6 @@ public function garbageCollection() {
   }
 
   /**
-   * 'Flattens' a tags array into an array of strings.
-   *
-   * @param array $tags
-   *   Associative array of tags to flatten.
-   *
-   * @return array
-   *   An indexed array of flattened tag identifiers.
-   */
-  protected function flattenTags(array $tags) {
-    if (isset($tags[0])) {
-      return $tags;
-    }
-
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags[] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags[] = "$namespace:$values";
-      }
-    }
-    return $flat_tags;
-  }
-
-  /**
    * Returns the sum total of validations for a given set of tags.
    *
    * @param array $tags
@@ -394,7 +366,7 @@ protected function flattenTags(array $tags) {
    * @return int
    *   Sum of all invalidations.
    *
-   * @see \Drupal\Core\Cache\DatabaseBackend::flattenTags()
+   * @see \Drupal\Core\Cache\Cache::flattenTags()
    */
   protected function checksumTags($flat_tags) {
     $tag_cache = &drupal_static('Drupal\Core\Cache\CacheBackendInterface::tagCache', array());
diff --git a/core/lib/Drupal/Core/Cache/MemoryBackend.php b/core/lib/Drupal/Core/Cache/MemoryBackend.php
index a7faca6..c571a5e 100644
--- a/core/lib/Drupal/Core/Cache/MemoryBackend.php
+++ b/core/lib/Drupal/Core/Cache/MemoryBackend.php
@@ -101,7 +101,7 @@ public function set($cid, $data, $expire = Cache::PERMANENT, array $tags = array
       'data' => $data,
       'created' => REQUEST_TIME,
       'expire' => $expire,
-      'tags' => $this->flattenTags($tags),
+      'tags' => Cache::flattenTags($tags),
     );
   }
 
@@ -123,7 +123,7 @@ public function deleteMultiple(array $cids) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::deleteTags().
    */
   public function deleteTags(array $tags) {
-    $flat_tags = $this->flattenTags($tags);
+    $flat_tags = Cache::flattenTags($tags);
     foreach ($this->cache as $cid => $item) {
       if (array_intersect($flat_tags, $item->tags)) {
         unset($this->cache[$cid]);
@@ -160,7 +160,7 @@ public function invalidateMultiple(array $cids) {
    * Implements Drupal\Core\Cache\CacheBackendInterface::invalidateTags().
    */
   public function invalidateTags(array $tags) {
-    $flat_tags = $this->flattenTags($tags);
+    $flat_tags = Cache::flattenTags($tags);
     foreach ($this->cache as $cid => $item) {
       if (array_intersect($flat_tags, $item->tags)) {
         $this->cache[$cid]->expire = REQUEST_TIME - 1;
@@ -178,34 +178,6 @@ public function invalidateAll() {
   }
 
   /**
-   * 'Flattens' a tags array into an array of strings.
-   *
-   * @param array $tags
-   *   Associative array of tags to flatten.
-   *
-   * @return array
-   *   An indexed array of strings.
-   */
-  protected function flattenTags(array $tags) {
-    if (isset($tags[0])) {
-      return $tags;
-    }
-
-    $flat_tags = array();
-    foreach ($tags as $namespace => $values) {
-      if (is_array($values)) {
-        foreach ($values as $value) {
-          $flat_tags["$namespace:$value"] = "$namespace:$value";
-        }
-      }
-      else {
-        $flat_tags["$namespace:$values"] = "$namespace:$values";
-      }
-    }
-    return $flat_tags;
-  }
-
-  /**
    * Implements Drupal\Core\Cache\CacheBackendInterface::isEmpty().
    */
   public function isEmpty() {
diff --git a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
index 69e80cd..5bcaabf 100644
--- a/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/HtmlViewSubscriber.php
@@ -77,7 +77,7 @@ public function onHtmlPage(GetResponseForControllerResultEvent $event) {
       // recommended.
       $response = new Response((string) $this->pageRenderer->render($page), $page->getStatusCode());
       if ($tags = $page->getCacheTags()) {
-        $response->headers->set('cache_tags', serialize($tags));
+        $response->headers->set('X-Drupal-Cache-Tags', implode(' ', \Drupal\Core\Cache\Cache::flattenTags($tags)));
       }
       if ($keys = $page->getCacheKeys()) {
         $response->headers->set('cache_keys', serialize($keys));
diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
index 6e0c509..d95a0a6 100644
--- a/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
+++ b/core/lib/Drupal/Core/Page/DefaultHtmlFragmentRenderer.php
@@ -51,6 +51,10 @@ public function render(HtmlFragment $fragment, $status_code = 200) {
     // Collect cache tags for all the content in all the regions on the page.
     $tags = drupal_render_collect_cache_tags($page_array);
 
+    // Enforce the generic "content" cache tag on all pages.
+    // @todo Remove the "content" cache tag. @see https://drupal.org/node/2124957
+    $tags['content'] = TRUE;
+
     // Build the HtmlPage object.
     $page = new HtmlPage('', array('tags' => $tags), $fragment->getTitle());
     $page = $this->preparePage($page, $page_array);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php
index 3ca1f19..ec596fa 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Bootstrap/PageCacheTest.php
@@ -65,6 +65,7 @@ function testPageCacheTags() {
     $cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
     $cid = sha1(implode(':', $cid_parts));
     $cache_entry = \Drupal::cache('page')->get($cid);
+    sort($cache_entry->tags);
     $this->assertIdentical($cache_entry->tags, array('content:1', 'system_test_cache_tags_page:1'));
 
     Cache::invalidateTags($tags);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php
index b245c34..1f0c39e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsIntegrationTest.php
@@ -96,14 +96,22 @@ function testPageCacheTags() {
    *   The expected cache tags for the page cache entry of the given $path.
    */
   protected function verifyPageCacheTags($path, $expected_tags) {
+    sort($expected_tags);
     $this->drupalGet($path);
     $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);
+    $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($path, array('absolute' => TRUE)), 'html');
     $cid = sha1(implode(':', $cid_parts));
     $cache_entry = \Drupal::cache('page')->get($cid);
-    $this->assertIdentical($cache_entry->tags, $expected_tags);
+    sort($cache_entry->tags);
+    $this->assertEqual($cache_entry->tags, $expected_tags);
   }
 
 }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php
index 194976a..4dfc88c 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Cache/PageCacheTagsTestBase.php
@@ -49,6 +49,8 @@ protected function verifyPageCache($path, $hit_or_miss, $tags = FALSE) {
       $cid_parts = array(url($path, array('absolute' => TRUE)), 'html');
       $cid = sha1(implode(':', $cid_parts));
       $cache_entry = \Drupal::cache('page')->get($cid);
+      sort($cache_entry->tags);
+      sort($tags);
       $this->assertIdentical($cache_entry->tags, $tags);
     }
   }
