diff --git a/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php
index 38007f5..8e8e885 100644
--- a/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/CacheRouterRebuildSubscriber.php
@@ -4,6 +4,7 @@
 
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Routing\RoutingEvents;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -16,8 +17,8 @@ class CacheRouterRebuildSubscriber implements EventSubscriberInterface {
    */
   public function onRouterFinished() {
     // Requested URLs that formerly gave a 403/404 may now be valid.
-    // Also invalidate all cached routing.
-    Cache::invalidateTags(['4xx-response', 'route_match']);
+    // Also invalidate all cached routing as well as every HTTP response.
+    Cache::invalidateTags(['4xx-response', 'route_match', 'http_response']);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
index 3db181a..fdd2a91 100644
--- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
@@ -89,6 +89,19 @@ public function __construct(LanguageManagerInterface $language_manager, ConfigFa
   }
 
   /**
+   * Sets extra headers on any responses, also subprocess ones.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
+   *   The event to process.
+   */
+  public function onAllResponds(FilterResponseEvent $event) {
+    $response = $event->getResponse();
+    // Always add the 'http_response' cache tag to be able to invalidate every
+    // response.
+    $response->getCacheableMetadata()->addCacheTags(['http_response']);
+  }
+
+  /**
    * Sets extra headers on successful responses.
    *
    * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
@@ -284,6 +297,7 @@ protected function setExpiresNoCache(Response $response) {
    */
   public static function getSubscribedEvents() {
     $events[KernelEvents::RESPONSE][] = array('onRespond');
+    $events[KernelEvents::RESPONSE][] = array('onAllResponds');
     return $events;
   }
 
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 9fd869a..57f4647 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -385,6 +385,7 @@ public function testBlockCacheTags() {
       'block_view',
       'config:block.block.powered',
       'config:user.role.anonymous',
+      'http_response',
       'rendered',
     );
     sort($expected_cache_tags);
@@ -426,6 +427,7 @@ public function testBlockCacheTags() {
       'config:block.block.powered',
       'config:block.block.powered-2',
       'config:user.role.anonymous',
+      'http_response',
       'rendered',
     );
     sort($expected_cache_tags);
diff --git a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
index 3ad4ad1..bba47d5 100644
--- a/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuCacheTagsTest.php
@@ -46,6 +46,7 @@ public function testMenuBlock() {
 
     // Verify a cache hit, but also the presence of the correct cache tags.
     $expected_tags = array(
+      'http_response',
       'rendered',
       'block_view',
       'config:block_list',
@@ -107,7 +108,7 @@ public function testMenuBlock() {
     $this->verifyPageCache($url, 'MISS');
 
     // Verify a cache hit.
-    $this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'rendered']);
+    $this->verifyPageCache($url, 'HIT', ['config:block_list', 'config:user.role.anonymous', 'http_response', 'rendered']);
   }
 
 }
diff --git a/core/modules/node/src/Tests/Views/FrontPageTest.php b/core/modules/node/src/Tests/Views/FrontPageTest.php
index b27c416..19cb659 100644
--- a/core/modules/node/src/Tests/Views/FrontPageTest.php
+++ b/core/modules/node/src/Tests/Views/FrontPageTest.php
@@ -265,7 +265,7 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
       $render_cache_tags
     );
     $expected_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags);
-    $expected_tags = Cache::mergeTags($expected_tags, ['rendered', 'config:user.role.anonymous', 'config:system.site']);
+    $expected_tags = Cache::mergeTags($expected_tags, ['http_response', 'rendered', 'config:user.role.anonymous', 'config:system.site']);
     $this->assertPageCacheContextsAndTags(
       Url::fromRoute('view.frontpage.page_1'),
       $cache_contexts,
@@ -331,7 +331,7 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
     $this->assertPageCacheContextsAndTags(
       Url::fromRoute('view.frontpage.page_1'),
       $cache_contexts,
-      Cache::mergeTags($first_page_output_cache_tags, ['rendered', 'config:user.role.anonymous'])
+      Cache::mergeTags($first_page_output_cache_tags, ['http_response', 'rendered', 'config:user.role.anonymous'])
     );
 
     // Second page.
@@ -350,6 +350,7 @@ protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
       'node_view',
       'user_view',
       'user:0',
+      'http_response',
       'rendered',
       // FinishResponseSubscriber adds this cache tag to responses that have the
       // 'user.permissions' cache context for anonymous users.
diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
index c8db96e..1933457 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
@@ -79,6 +79,7 @@ function testPageCacheTags() {
 
     // Full node page 1.
     $this->assertPageCacheContextsAndTags($node_1->urlInfo(), $cache_contexts, array(
+      'http_response',
       'rendered',
       'block_view',
       'config:block_list',
@@ -119,6 +120,7 @@ function testPageCacheTags() {
 
     // Full node page 2.
     $this->assertPageCacheContextsAndTags($node_2->urlInfo(), $cache_contexts, array(
+      'http_response',
       'rendered',
       'block_view',
       'config:block_list',
diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php
index 1ec9282..68f97a3 100644
--- a/core/modules/page_cache/src/Tests/PageCacheTest.php
+++ b/core/modules/page_cache/src/Tests/PageCacheTest.php
@@ -64,6 +64,7 @@ function testPageCacheTags() {
     $expected_tags = array(
       'config:user.role.anonymous',
       'pre_render',
+      'http_response',
       'rendered',
       'system_test_cache_tags_page',
     );
@@ -95,6 +96,7 @@ function testPageCacheTagsIndependentFromCacheabilityHeaders() {
     $expected_tags = array(
       'config:user.role.anonymous',
       'pre_render',
+      'http_response',
       'rendered',
       'system_test_cache_tags_page',
     );
diff --git a/core/modules/rest/src/Entity/RestResourceConfig.php b/core/modules/rest/src/Entity/RestResourceConfig.php
index bb83584..60fe55a 100644
--- a/core/modules/rest/src/Entity/RestResourceConfig.php
+++ b/core/modules/rest/src/Entity/RestResourceConfig.php
@@ -3,6 +3,7 @@
 namespace Drupal\rest\Entity;
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
+use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Plugin\DefaultSingleLazyPluginCollection;
 use Drupal\rest\RestResourceConfigInterface;
 
@@ -255,4 +256,13 @@ protected function normalizeRestMethod($method) {
     return strtoupper($method);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function postSave(EntityStorageInterface $storage, $update = TRUE) {
+    parent::postSave($storage, $update);
+
+    \Drupal::service('router.builder')->setRebuildNeeded();
+  }
+
 }
diff --git a/core/modules/rest/src/Tests/RESTTestBase.php b/core/modules/rest/src/Tests/RESTTestBase.php
index 271df51..405493b 100644
--- a/core/modules/rest/src/Tests/RESTTestBase.php
+++ b/core/modules/rest/src/Tests/RESTTestBase.php
@@ -420,8 +420,7 @@ protected function enableService($resource_type, $method = 'GET', $format = NULL
    * Rebuilds routing caches.
    */
   protected function rebuildCache() {
-    // Rebuild routing cache, so that the REST API paths are available.
-    $this->container->get('router.builder')->rebuild();
+    $this->container->get('router.builder')->rebuildIfNeeded();
   }
 
   /**
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php
index 94c0b04..4cf4eb5 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Block/BlockResourceTestBase.php
@@ -122,9 +122,9 @@ protected function getExpectedCacheContexts() {
   protected function getExpectedCacheTags() {
     // Because the 'user.permissions' cache context is missing, the cache tag
     // for the anonymous user role is never added automatically.
-    return array_filter(parent::getExpectedCacheTags(), function ($tag) {
+    return array_values(array_filter(parent::getExpectedCacheTags(), function ($tag) {
       return $tag !== 'config:user.role.anonymous';
-    });
+    }));
   }
 
 }
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 48ed438..dbb2d2a 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -196,9 +196,6 @@ public function setUp() {
       }
       $this->entity->save();
     }
-
-    // @todo Remove this in https://www.drupal.org/node/2815845.
-    drupal_flush_all_caches();
   }
 
   /**
@@ -255,6 +252,7 @@ protected function getExpectedCacheTags() {
     if (!static::$auth) {
       $expected_cache_tags[] = 'config:user.role.anonymous';
     }
+    $expected_cache_tags[] = 'http_response';
     return Cache::mergeTags($expected_cache_tags, $this->entity->getCacheTags());
   }
 
diff --git a/core/modules/rest/tests/src/Functional/ResourceTestBase.php b/core/modules/rest/tests/src/Functional/ResourceTestBase.php
index 49ef8e5..1dc7e96 100644
--- a/core/modules/rest/tests/src/Functional/ResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/ResourceTestBase.php
@@ -152,8 +152,7 @@ protected function provisionResource($resource_type, $formats = [], $authenticat
         'authentication' => $authentication,
       ]
     ])->save();
-    // @todo Remove this in https://www.drupal.org/node/2815845.
-    drupal_flush_all_caches();
+    \Drupal::service('router.builder')->rebuild();
   }
 
   /**
diff --git a/core/modules/search/src/Tests/SearchPageCacheTagsTest.php b/core/modules/search/src/Tests/SearchPageCacheTagsTest.php
index 3a7bc67..4937369 100644
--- a/core/modules/search/src/Tests/SearchPageCacheTagsTest.php
+++ b/core/modules/search/src/Tests/SearchPageCacheTagsTest.php
@@ -70,6 +70,7 @@ function testSearchText() {
     $this->assertCacheTag('node:1');
     $this->assertCacheTag('user:2');
     $this->assertCacheTag('rendered');
+    $this->assertCacheTag('http_response');
     $this->assertCacheTag('node_list');
 
     // Updating a node should invalidate the search plugin's index cache tag.
@@ -83,6 +84,7 @@ function testSearchText() {
     $this->assertCacheTag('node:1');
     $this->assertCacheTag('user:2');
     $this->assertCacheTag('rendered');
+    $this->assertCacheTag('http_response');
     $this->assertCacheTag('node_list');
 
     // Deleting a node should invalidate the search plugin's index cache tag.
@@ -172,6 +174,7 @@ public function testSearchTagsBubbling() {
       'config:search.page.node_search',
       'search_index',
       'search_index:node_search',
+      'http_response',
       'rendered',
       'node_list',
     ];
@@ -190,10 +193,8 @@ public function testSearchTagsBubbling() {
       'node:2',
       'user:3',
       'node_view',
-      'config:filter.format.plain_text',
     ]);
-    $cache_tags = $this->drupalGetHeader('X-Drupal-Cache-Tags');
-    $this->assertEqual(explode(' ', $cache_tags), $expected_cache_tags);
+    $this->assertCacheTags($expected_cache_tags);
 
     // Only get the new node in the search results, should result in node:1,
     // node:2 and user:3 as cache tags even though only node:1 is shown. This is
@@ -208,8 +209,7 @@ public function testSearchTagsBubbling() {
       'user:3',
       'node_view',
     ]);
-    $cache_tags = $this->drupalGetHeader('X-Drupal-Cache-Tags');
-    $this->assertEqual(explode(' ', $cache_tags), $expected_cache_tags);
+    $this->assertCacheTags($expected_cache_tags);
   }
 
 }
diff --git a/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php b/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php
index 94ea1ac..266ffbd 100644
--- a/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php
+++ b/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php
@@ -122,8 +122,11 @@ protected function debugCacheTags(array $actual_tags, array $expected_tags) {
    */
   protected function assertCacheTags(array $expected_tags, $include_default_tags = TRUE) {
     // The anonymous role cache tag is only added if the user is anonymous.
-    if ($include_default_tags && \Drupal::currentUser()->isAnonymous()) {
-      $expected_tags = Cache::mergeTags($expected_tags, ['config:user.role.anonymous']);
+    if ($include_default_tags) {
+      if (\Drupal::currentUser()->isAnonymous()) {
+        $expected_tags = Cache::mergeTags($expected_tags, ['config:user.role.anonymous']);
+      }
+      $expected_tags[] = 'http_response';
     }
     $actual_tags = $this->getCacheHeaderValues('X-Drupal-Cache-Tags');
     sort($expected_tags);
diff --git a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
index 9ecefad..d0751bd 100644
--- a/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityCacheTagsTestBase.php
@@ -343,7 +343,7 @@ public function testReferencedEntity() {
     // 'user.permissions' is a required cache context, and responses that vary
     // by this cache context when requested by anonymous users automatically
     // also get this cache tag, to ensure correct invalidation.
-    $page_cache_tags = Cache::mergeTags(['rendered'], ['config:user.role.anonymous']);
+    $page_cache_tags = Cache::mergeTags(['http_response', 'rendered'], ['config:user.role.anonymous']);
     // If the block module is used, the Block page display variant is used,
     // which adds the block config entity type's list cache tags.
     $page_cache_tags = Cache::mergeTags($page_cache_tags, \Drupal::moduleHandler()->moduleExists('block') ? ['config:block_list'] : []);
@@ -401,6 +401,7 @@ public function testReferencedEntity() {
       $context_metadata = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts);
       $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, $context_metadata->getCacheTags());
     }
+    debug($referencing_entity_cache_tags);
     $this->verifyRenderCache($cid, $referencing_entity_cache_tags, $redirected_cid);
 
     $this->pass("Test non-referencing entity.", 'Debug');
@@ -641,7 +642,7 @@ public function testReferencedEntity() {
 
     // Verify cache hits.
     $referencing_entity_cache_tags = Cache::mergeTags($this->referencingEntity->getCacheTags(), \Drupal::entityManager()->getViewBuilder('entity_test')->getCacheTags());
-    $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['rendered']);
+    $referencing_entity_cache_tags = Cache::mergeTags($referencing_entity_cache_tags, ['http_response', 'rendered']);
 
     $nonempty_entity_listing_cache_tags = Cache::mergeTags($this->entity->getEntityType()->getListCacheTags(), $this->getAdditionalCacheTagsForEntityListing());
     $nonempty_entity_listing_cache_tags = Cache::mergeTags($nonempty_entity_listing_cache_tags, $page_cache_tags);
diff --git a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
index f6da0a2..10c5031 100644
--- a/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Entity/EntityWithUriCacheTagsTestBase.php
@@ -53,7 +53,7 @@ public function testEntityUri() {
       }
       $expected_cache_tags = Cache::mergeTags($cache_tag, $view_cache_tag);
       $expected_cache_tags = Cache::mergeTags($expected_cache_tags, $this->getAdditionalCacheTagsForEntity($this->entity));
-      $expected_cache_tags = Cache::mergeTags($expected_cache_tags, array($render_cache_tag));
+      $expected_cache_tags = Cache::mergeTags($expected_cache_tags, array('http_response', $render_cache_tag));
       $this->verifyRenderCache($cid, $expected_cache_tags, $redirected_cid);
     }
 
diff --git a/core/modules/system/src/Tests/Routing/RouterTest.php b/core/modules/system/src/Tests/Routing/RouterTest.php
index 1b33baf..202762b 100644
--- a/core/modules/system/src/Tests/Routing/RouterTest.php
+++ b/core/modules/system/src/Tests/Routing/RouterTest.php
@@ -45,7 +45,7 @@ public function testFinishResponseSubscriber() {
     // Check expected headers from FinishResponseSubscriber.
     $headers = $this->drupalGetHeaders();
     $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', $expected_cache_contexts));
-    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous rendered');
+    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous http_response rendered');
     // Confirm that the page wrapping is being added, so we're not getting a
     // raw body returned.
     $this->assertRaw('</html>', 'Page markup was found.');
@@ -60,12 +60,12 @@ public function testFinishResponseSubscriber() {
     $this->drupalGet('router_test/test18');
     $headers = $this->drupalGetHeaders();
     $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', Cache::mergeContexts($renderer_required_cache_contexts, ['url'])));
-    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo rendered');
+    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo http_response rendered');
     // 2. controller result: render array, per-role cacheable route access.
     $this->drupalGet('router_test/test19');
     $headers = $this->drupalGetHeaders();
     $this->assertEqual($headers['x-drupal-cache-contexts'], implode(' ', Cache::mergeContexts($renderer_required_cache_contexts, ['url', 'user.roles'])));
-    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo rendered');
+    $this->assertEqual($headers['x-drupal-cache-tags'], 'config:user.role.anonymous foo http_response rendered');
     // 3. controller result: Response object, globally cacheable route access.
     $this->drupalGet('router_test/test1');
     $headers = $this->drupalGetHeaders();
@@ -79,13 +79,13 @@ public function testFinishResponseSubscriber() {
     // 5. controller result: CacheableResponse object, globally cacheable route access.
     $this->drupalGet('router_test/test21');
     $headers = $this->drupalGetHeaders();
-    $this->assertEqual($headers['x-drupal-cache-contexts'], '');
-    $this->assertEqual($headers['x-drupal-cache-tags'], '');
+    $this->assertEqual($headers['x-drupal-cache-contexts'], 'http_response');
+    $this->assertEqual($headers['x-drupal-cache-tags'], 'http_response');
     // 6. controller result: CacheableResponse object, per-role cacheable route access.
     $this->drupalGet('router_test/test22');
     $headers = $this->drupalGetHeaders();
     $this->assertEqual($headers['x-drupal-cache-contexts'], 'user.roles');
-    $this->assertEqual($headers['x-drupal-cache-tags'], '');
+    $this->assertEqual($headers['x-drupal-cache-tags'], 'http_response');
 
     // Finally, verify that the X-Drupal-Cache-Contexts and X-Drupal-Cache-Tags
     // headers are not sent when their container parameter is set to FALSE.
diff --git a/core/modules/views/src/Tests/GlossaryTest.php b/core/modules/views/src/Tests/GlossaryTest.php
index 508a4eb..2a5bc90 100644
--- a/core/modules/views/src/Tests/GlossaryTest.php
+++ b/core/modules/views/src/Tests/GlossaryTest.php
@@ -95,6 +95,7 @@ public function testGlossaryView() {
         'node_list',
         'user:0',
         'user_list',
+        'http_response',
         'rendered',
         // FinishResponseSubscriber adds this cache tag to responses that have the
         // 'user.permissions' cache context for anonymous users.
diff --git a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
index eb315af..e0903a2 100644
--- a/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
+++ b/core/tests/Drupal/FunctionalTests/BrowserTestBaseTest.php
@@ -40,7 +40,7 @@ public function testGoTo() {
     $this->assertNotContains('</html>', $text);
 
     // Response includes cache tags that we can assert.
-    $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Tags', 'rendered');
+    $this->assertSession()->responseHeaderEquals('X-Drupal-Cache-Tags', 'rendered http_response');
 
     // Test that we can read the JS settings.
     $js_settings = $this->getDrupalSettings();
