diff --git a/core/core.services.yml b/core/core.services.yml
index a7a2b3b..2d75920 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -220,7 +220,7 @@ services:
     arguments: ['@request_stack', '@url_generator']
   form_cache:
     class: Drupal\Core\Form\FormCache
-    arguments: ['@app.root', '@keyvalue.expirable', '@module_handler', '@current_user', '@csrf_token', '@logger.channel.form', '@config.factory', '@request_stack', '@page_cache_request_policy']
+    arguments: ['@app.root', '@keyvalue.expirable', '@module_handler', '@current_user', '@csrf_token', '@logger.channel.form', '@request_stack', '@page_cache_request_policy']
     public: false  # Private to form_builder
   keyvalue:
     class: Drupal\Core\KeyValueStore\KeyValueFactory
@@ -503,11 +503,6 @@ services:
     arguments: ['@settings']
     tags:
       - { name: http_middleware, priority: 300 }
-  http_middleware.page_cache:
-    class: Drupal\Core\StackMiddleware\PageCache
-    arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy']
-    tags:
-      - { name: http_middleware, priority: 200 }
   http_middleware.kernel_pre_handle:
     class: Drupal\Core\StackMiddleware\KernelPreHandle
     arguments: ['@kernel']
diff --git a/core/lib/Drupal/Core/Form/FormCache.php b/core/lib/Drupal/Core/Form/FormCache.php
index ea38fd2..a35000d 100644
--- a/core/lib/Drupal/Core/Form/FormCache.php
+++ b/core/lib/Drupal/Core/Form/FormCache.php
@@ -10,7 +10,6 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Access\CsrfTokenGenerator;
-use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\KeyValueStore\KeyValueExpirableFactoryInterface;
 use Drupal\Core\PageCache\RequestPolicyInterface;
@@ -104,20 +103,17 @@ class FormCache implements FormCacheInterface {
    *   The CSRF token generator.
    * @param \Psr\Log\LoggerInterface $logger
    *   A logger instance.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The configuration factory.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   The request stack.
    * @param \Drupal\Core\PageCache\RequestPolicyInterface $request_policy
    *   A policy rule determining the cacheability of a request.
    */
-  public function __construct($root, KeyValueExpirableFactoryInterface $key_value_expirable_factory, ModuleHandlerInterface $module_handler, AccountInterface $current_user, CsrfTokenGenerator $csrf_token, LoggerInterface $logger, ConfigFactoryInterface $config_factory, RequestStack $request_stack, RequestPolicyInterface $request_policy) {
+  public function __construct($root, KeyValueExpirableFactoryInterface $key_value_expirable_factory, ModuleHandlerInterface $module_handler, AccountInterface $current_user, CsrfTokenGenerator $csrf_token, LoggerInterface $logger, RequestStack $request_stack, RequestPolicyInterface $request_policy) {
     $this->root = $root;
     $this->keyValueExpirableFactory = $key_value_expirable_factory;
     $this->moduleHandler = $module_handler;
     $this->currentUser = $current_user;
     $this->logger = $logger;
-    $this->configFactory = $config_factory;
     $this->csrfToken = $csrf_token;
     $this->requestStack = $request_stack;
     $this->requestPolicy = $request_policy;
@@ -210,11 +206,6 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state)
       $this->keyValueExpirableFactory->get('form')->setWithExpire($form_build_id, $form, $expire);
     }
 
-    // Cache form state.
-    if ($this->configFactory->get('system.performance')->get('cache.page.use_internal') && $this->isPageCacheable()) {
-      $form_state->addBuildInfo('immutable', TRUE);
-    }
-
     // Store the known list of safe strings for form re-use.
     // @todo Ensure we are not storing an excessively large string list in:
     //   https://www.drupal.org/node/2295823
@@ -225,14 +216,4 @@ public function setCache($form_build_id, $form, FormStateInterface $form_state)
     }
   }
 
-  /**
-   * Checks if the page is cacheable.
-   *
-   * @return bool
-   *   TRUE is the page is cacheable, FALSE if not.
-   */
-  protected function isPageCacheable() {
-    return ($this->requestPolicy->check($this->requestStack->getCurrentRequest()) === RequestPolicyInterface::ALLOW);
-  }
-
 }
diff --git a/core/lib/Drupal/Core/StackMiddleware/PageCache.php b/core/lib/Drupal/Core/StackMiddleware/PageCache.php
deleted file mode 100644
index d4ec291..0000000
--- a/core/lib/Drupal/Core/StackMiddleware/PageCache.php
+++ /dev/null
@@ -1,341 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\Core\StackMiddleware\PageCache.
- */
-
-namespace Drupal\Core\StackMiddleware;
-
-use Drupal\Core\Cache\Cache;
-use Drupal\Core\Cache\CacheBackendInterface;
-use Drupal\Core\PageCache\RequestPolicyInterface;
-use Drupal\Core\PageCache\ResponsePolicyInterface;
-use Drupal\Core\Site\Settings;
-use Symfony\Component\HttpFoundation\BinaryFileResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpFoundation\Response;
-use Symfony\Component\HttpFoundation\StreamedResponse;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
-/**
- * Executes the page caching before the main kernel takes over the request.
- */
-class PageCache implements HttpKernelInterface {
-
-  /**
-   * The wrapped HTTP kernel.
-   *
-   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
-   */
-  protected $httpKernel;
-
-  /**
-   * The cache bin.
-   *
-   * @var \Drupal\Core\Cache\CacheBackendInterface.
-   */
-  protected $cache;
-
-  /**
-   * A policy rule determining the cacheability of a request.
-   *
-   * @var \Drupal\Core\PageCache\RequestPolicyInterface
-   */
-  protected $requestPolicy;
-
-  /**
-   * A policy rule determining the cacheability of the response.
-   *
-   * @var \Drupal\Core\PageCache\ResponsePolicyInterface
-   */
-  protected $responsePolicy;
-
-  /**
-   * Constructs a ReverseProxyMiddleware object.
-   *
-   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
-   *   The decorated kernel.
-   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
-   *   The cache bin.
-   * @param \Drupal\Core\PageCache\RequestPolicyInterface $request_policy
-   *   A policy rule determining the cacheability of a request.
-   * @param \Drupal\Core\PageCache\ResponsePolicyInterface $response_policy
-   *   A policy rule determining the cacheability of the response.
-   */
-  public function __construct(HttpKernelInterface $http_kernel, CacheBackendInterface $cache, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy) {
-    $this->httpKernel = $http_kernel;
-    $this->cache = $cache;
-    $this->requestPolicy = $request_policy;
-    $this->responsePolicy = $response_policy;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
-    if ($type !== static::MASTER_REQUEST) {
-      // Only allow page caching on master request.
-      $cache_enabled = FALSE;
-    }
-    elseif (Settings::get('page_cache_without_database')) {
-      // Check for a cache mode force from settings.php.
-      $cache_enabled = TRUE;
-    }
-    else {
-      $config = $this->config('system.performance');
-      $cache_enabled = $config->get('cache.page.use_internal');
-    }
-
-    if ($cache_enabled && $this->requestPolicy->check($request) === RequestPolicyInterface::ALLOW) {
-      $response = $this->lookup($request, $type, $catch);
-    }
-    else {
-      $response = $this->pass($request, $type, $catch);
-    }
-
-    return $response;
-  }
-
-  /**
-   * Sidesteps the page cache and directly forwards a request to the backend.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A request object.
-   * @param int $type
-   *   The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
-   *   HttpKernelInterface::SUB_REQUEST)
-   * @param bool $catch
-   *   Whether to catch exceptions or not
-   *
-   * @returns \Symfony\Component\HttpFoundation\Response $response
-   *   A response object.
-   */
-  protected function pass(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
-    return $this->httpKernel->handle($request, $type, $catch);
-  }
-
-  /**
-   * Retrieves a response from the cache or fetches it from the backend.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A request object.
-   * @param int $type
-   *   The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
-   *   HttpKernelInterface::SUB_REQUEST)
-   * @param bool $catch
-   *   Whether to catch exceptions or not
-   *
-   * @returns \Symfony\Component\HttpFoundation\Response $response
-   *   A response object.
-   */
-  protected function lookup(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
-    if ($response = $this->get($request)) {
-      $response->headers->set('X-Drupal-Cache', 'HIT');
-    }
-    else {
-      $response = $this->fetch($request, $type, $catch);
-    }
-
-    // Only allow caching in the browser and prevent that the response is stored
-    // by an external proxy server when the following conditions apply:
-    // 1. There is a session cookie on the request.
-    // 2. The Vary: Cookie header is on the response.
-    // 3. The Cache-Control header does not contain the no-cache directive.
-    if ($request->cookies->has(session_name()) &&
-      in_array('Cookie', $response->getVary()) &&
-      !$response->headers->hasCacheControlDirective('no-cache')) {
-
-      $response->setPrivate();
-    }
-
-    // Negotiate whether to use compression.
-    if (extension_loaded('zlib') && $response->headers->get('Content-Encoding') === 'gzip') {
-      if (strpos($request->headers->get('Accept-Encoding'), 'gzip') !== FALSE) {
-        // The response content is already gzip'ed, so make sure
-        // zlib.output_compression does not compress it once more.
-        ini_set('zlib.output_compression', '0');
-      }
-      else {
-        // The client does not support compression. Decompress the content and
-        // remove the Content-Encoding header.
-        $content = $response->getContent();
-        $content = gzinflate(substr(substr($content, 10), 0, -8));
-        $response->setContent($content);
-        $response->headers->remove('Content-Encoding');
-      }
-    }
-
-    // Perform HTTP revalidation.
-    // @todo Use Response::isNotModified() as per https://drupal.org/node/2259489
-    $last_modified = $response->getLastModified();
-    if ($last_modified) {
-      // See if the client has provided the required HTTP headers.
-      $if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE;
-      $if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE;
-
-      if ($if_modified_since && $if_none_match
-        && $if_none_match == $response->getEtag() // etag must match
-        && $if_modified_since == $last_modified->getTimestamp()) {  // if-modified-since must match
-        $response->setStatusCode(304);
-        $response->setContent(NULL);
-
-        // In the case of a 304 response, certain headers must be sent, and the
-        // remaining may not (see RFC 2616, section 10.3.5).
-        foreach (array_keys($response->headers->all()) as $name) {
-          if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) {
-            $response->headers->remove($name);
-          }
-        }
-      }
-    }
-
-    return $response;
-  }
-
-  /**
-   * Fetches a response from the backend and stores it in the cache.
-   *
-   * If page_compression is enabled, a gzipped version of the page is stored in
-   * the cache to avoid compressing the output on each request. The cache entry
-   * is unzipped in the relatively rare event that the page is requested by a
-   * client without gzip support.
-   *
-   * Page compression requires the PHP zlib extension
-   * (http://php.net/manual/ref.zlib.php).
-   *
-   * @see drupal_page_header()
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A request object.
-   * @param int $type
-   *   The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
-   *   HttpKernelInterface::SUB_REQUEST)
-   * @param bool $catch
-   *   Whether to catch exceptions or not
-   *
-   * @returns \Symfony\Component\HttpFoundation\Response $response
-   *   A response object.
-   */
-  protected function fetch(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
-    $response = $this->httpKernel->handle($request, $type, $catch);
-
-    // Currently it is not possible to cache some types of responses. Therefore
-    // exclude binary file responses (generated files, e.g. images with image
-    // styles) and streamed responses (files directly read from the disk).
-    // see: https://github.com/symfony/symfony/issues/9128#issuecomment-25088678
-    if ($response instanceof BinaryFileResponse || $response instanceof StreamedResponse) {
-      return $response;
-    }
-
-    if ($this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) {
-      return $response;
-    }
-
-    // Check if the current page may be compressed.
-    if (extension_loaded('zlib') && !$response->headers->get('Content-Encoding') && $this->config('system.performance')->get('response.gzip')) {
-      $content = $response->getContent();
-      if ($content) {
-        $response->setContent(gzencode($content, 9, FORCE_GZIP));
-        $response->headers->set('Content-Encoding', 'gzip');
-      }
-
-      // When page compression is enabled, ensure that proxy caches will record
-      // and deliver different versions of a page depending on whether the
-      // client supports gzip or not.
-      $response->setVary('Accept-Encoding', FALSE);
-    }
-
-    // Use the actual timestamp from an Expires header, if available.
-    $date = $response->getExpires();
-    $expire = ($date > (new \DateTime())) ? $date->getTimestamp() : Cache::PERMANENT;
-
-    $tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags'));
-    $this->set($request, $response, $expire, $tags);
-
-    // Mark response as a cache miss.
-    $response->headers->set('X-Drupal-Cache', 'MISS');
-
-    return $response;
-  }
-
-  /**
-   * Returns a response object from the page cache.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A request object.
-   * @param bool $allow_invalid
-   *   (optional) If TRUE, a cache item may be returned even if it is expired or
-   *   has been invalidated. Such items may sometimes be preferred, if the
-   *   alternative is recalculating the value stored in the cache, especially
-   *   if another concurrent request is already recalculating the same value.
-   *   The "valid" property of the returned object indicates whether the item is
-   *   valid or not. Defaults to FALSE.
-   *
-   * @return \Symfony\Component\HttpFoundation\Response|false
-   *   The cached response or FALSE on failure.
-   */
-  protected function get(Request $request, $allow_invalid = FALSE) {
-    $cid = $this->getCacheId($request);
-    if ($cache = $this->cache->get($cid, $allow_invalid)) {
-      return $cache->data;
-    }
-  }
-
-  /**
-   * Stores a response object in the page cache.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A request object.
-   * @param \Symfony\Component\HttpFoundation\Response $response
-   *   The response to store in the cache.
-   * @param int $expire
-   *   One of the following values:
-   *   - CacheBackendInterface::CACHE_PERMANENT: Indicates that the item should
-   *     not be removed unless it is deleted explicitly.
-   *   - A Unix timestamp: Indicates that the item will be considered invalid
-   *     after this time, i.e. it will not be returned by get() unless
-   *     $allow_invalid has been set to TRUE. When the item has expired, it may
-   *     be permanently deleted by the garbage collector at any time.
-   * @param array $tags
-   *   An array of tags to be stored with the cache item. These should normally
-   *   identify objects used to build the cache item, which should trigger
-   *   cache invalidation when updated. For example if a cached item represents
-   *   a node, both the node ID and the author's user ID might be passed in as
-   *   tags. For example array('node' => array(123), 'user' => array(92)).
-   */
-  protected function set(Request $request, Response $response, $expire, array $tags) {
-    $cid = $this->getCacheId($request);
-    $this->cache->set($cid, $response, $expire, $tags);
-  }
-
-  /**
-   * Gets the page cache ID for this request.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   A request object.
-   *
-   * @return string
-   *   The cache ID for this request.
-   */
-  protected function getCacheId(Request $request) {
-    $cid_parts = array(
-      $request->getUri(),
-      $request->getRequestFormat(),
-    );
-    return implode(':', $cid_parts);
-  }
-
-  /**
-   * Wraps Drupal::config().
-   *
-   * Config factory is not injected into this class in order to prevent
-   * premature initialization of config storage (database).
-   *
-   * @see \Drupal::config()
-   */
-  protected function config($name) {
-    return \Drupal::config($name);
-  }
-
-}
diff --git a/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php b/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php
index 44c14f1..942aa1d 100644
--- a/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php
+++ b/core/modules/basic_auth/src/Tests/Authentication/BasicAuthTest.php
@@ -24,7 +24,7 @@ class BasicAuthTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('basic_auth', 'router_test', 'locale');
+  public static $modules = array('basic_auth', 'router_test', 'locale', 'page_cache');
 
   /**
    * Test http basic authentication.
@@ -32,7 +32,6 @@ class BasicAuthTest extends WebTestBase {
   public function testBasicAuth() {
     // Enable page caching.
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
 
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index e6709da..498befb 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -22,6 +22,11 @@
 class BlockTest extends BlockTestBase {
 
   /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['page_cache'];
+
+  /**
    * Tests block visibility.
    */
   function testBlockVisibility() {
@@ -309,7 +314,6 @@ public function testBlockCacheTags() {
 
     // Enable page caching.
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
 
diff --git a/core/modules/block/src/Tests/BlockTestBase.php b/core/modules/block/src/Tests/BlockTestBase.php
index 1f3cb74..307a435 100644
--- a/core/modules/block/src/Tests/BlockTestBase.php
+++ b/core/modules/block/src/Tests/BlockTestBase.php
@@ -19,7 +19,7 @@
    *
    * @var array
    */
-  public static $modules = array('block', 'filter', 'test_page_test', 'help', 'block_test');
+  protected static $modules = array('block', 'filter', 'test_page_test', 'help', 'block_test');
 
   /**
    * A list of theme regions to test.
diff --git a/core/modules/file/src/Tests/DownloadTest.php b/core/modules/file/src/Tests/DownloadTest.php
index 9de1b3a..4df65e5 100644
--- a/core/modules/file/src/Tests/DownloadTest.php
+++ b/core/modules/file/src/Tests/DownloadTest.php
@@ -56,8 +56,8 @@ public function testPrivateFileTransferWithoutPageCache() {
    */
   public function testPrivateFileTransferWithPageCache() {
     // Turn on page caching and rerun the test.
+    $this->container->get('module_installer')->install(['page_cache']);
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
 
diff --git a/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php b/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php
index abe8ffb..88cf620 100644
--- a/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php
+++ b/core/modules/image/src/Tests/ImageStylesPathAndUrlTest.php
@@ -247,8 +247,8 @@ function doImageStyleUrlAndPathTests($scheme, $clean_url = TRUE, $extra_slash =
    */
   protected function enablePageCache() {
     // Turn on page caching and rerun the test.
+    $this->container->get('module_installer')->install(['page_cache']);
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
   }
diff --git a/core/modules/language/src/LanguageNegotiator.php b/core/modules/language/src/LanguageNegotiator.php
index bc924fb..2354d88 100644
--- a/core/modules/language/src/LanguageNegotiator.php
+++ b/core/modules/language/src/LanguageNegotiator.php
@@ -188,20 +188,7 @@ protected function negotiateLanguage($type, $method_id) {
     $method = $this->negotiatorManager->getDefinition($method_id);
 
     if (!isset($method['types']) || in_array($type, $method['types'])) {
-
-      // Check for a cache mode force from settings.php.
-      if ($this->settings->get('page_cache_without_database')) {
-        $cache_enabled = TRUE;
-      }
-      else {
-        $cache_enabled = $this->configFactory->get('system.performance')->get('cache.page.use_internal');
-      }
-
-      // If the language negotiation method has no cache preference or this is
-      // satisfied we can execute the callback.
-      if ($cache = !isset($method['cache']) || $this->currentUser->isAuthenticated() || $method['cache'] == $cache_enabled) {
-        $langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->requestStack->getCurrentRequest());
-      }
+      $langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->requestStack->getCurrentRequest());
     }
 
     $languages = $this->languageManager->getLanguages();
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
index e9fc38e..68e1b10 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -17,7 +17,6 @@
  * @Plugin(
  *   id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser::METHOD_ID,
  *   weight = -2,
- *   cache = 0,
  *   name = @Translation("Browser"),
  *   description = @Translation("Language from the browser's language settings."),
  *   config_route_name = "language.negotiation_browser"
@@ -41,6 +40,10 @@ public function getLangcode(Request $request = NULL) {
       $langcodes = array_keys($this->languageManager->getLanguages());
       $mappings = $this->config->get('language.mappings')->get();
       $langcode = UserAgent::getBestMatchingLangcode($http_accept_language, $langcodes, $mappings);
+      // Internal page cache with multiple languages and browser negotiation
+      // could lead to wrong cached sites. Therefore disabling the internal
+      // page cache.
+      \Drupal::service('page_cache_kill_switch')->trigger();
     }
 
     return $langcode;
diff --git a/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_performance.yml b/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_performance.yml
index 2b6f478..c27f3a9 100644
--- a/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_performance.yml
+++ b/core/modules/migrate_drupal/config/install/migrate.migration.d6_system_performance.yml
@@ -14,7 +14,6 @@ process:
   'css/preprocess': preprocess_css
   'js/preprocess': preprocess_js
   'cache/page/max_age': cache_lifetime
-  'cache/page/use_internal': cache
   'response/gzip': page_compression
 destination:
   plugin: config
diff --git a/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemPerformanceTest.php b/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemPerformanceTest.php
index 168e2e6..2f13720 100644
--- a/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemPerformanceTest.php
+++ b/core/modules/migrate_drupal/src/Tests/d6/MigrateSystemPerformanceTest.php
@@ -40,8 +40,6 @@ public function testSystemPerformance() {
     $this->assertIdentical(FALSE, $config->get('css.preprocess'));
     $this->assertIdentical(FALSE, $config->get('js.preprocess'));
     $this->assertIdentical(0, $config->get('cache.page.max_age'));
-    $this->assertIdentical(TRUE, $config->get('cache.page.use_internal'));
-    $this->assertIdentical(TRUE, $config->get('response.gzip'));
   }
 
 }
diff --git a/core/modules/page_cache/page_cache.info.yml b/core/modules/page_cache/page_cache.info.yml
new file mode 100644
index 0000000..af72fab
--- /dev/null
+++ b/core/modules/page_cache/page_cache.info.yml
@@ -0,0 +1,6 @@
+name: Internal page cache
+type: module
+description: 'Caches pages for anonymous users. Works well for small to medium-sized websites.'
+package: Core
+version: VERSION
+core: 8.x
diff --git a/core/modules/page_cache/page_cache.module b/core/modules/page_cache/page_cache.module
new file mode 100644
index 0000000..6322363
--- /dev/null
+++ b/core/modules/page_cache/page_cache.module
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Caches responses for anonymous users, request and response policies allowing.
+ */
+
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\PageCache\RequestPolicyInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Implements hook_help().
+ */
+function page_cache_help($route_name, RouteMatchInterface $route_match) {
+  switch ($route_name) {
+    case 'help.page.page_cache':
+      $output = '<h3>' . t('About') . '</h3>';
+      $output .= '<p>' . t('The Internal page cache module caches pages for anonymous users in the database.') . '</p>';
+      $output .= '<h3>' . t('Uses') . '</h3>';
+      $output .= '<dl>';
+      $output .= '<dt>' . t('Speeding up your site') . '</dt>';
+      $output .= '<dd>';
+      $output .= '<p>' . t('Pages requested by anonymous users are stored the first time they are requested and then reused; depending on your site configuration and the amount of your web traffic tied to anonymous visitors, the caching system may significantly increase the speed of your site.');
+      $output .= '<p>' . t('(For authenticated users, pages need to be assembled for each user individually, but anonymous users all get the exact same version of each page.)') . '</p>';
+      $output .= '</dd>';
+      $output .= '<dt>' . t('Configuring Internal page cache') . '</dt>';
+      $output .= '<dd>';
+      $output .= '<p>' . t('On the <a href="@cache-settings">Performance settings page</a>, you can configure how long browsers and proxies may cache pages, that setting is also respected by the Internal page cache module. There is no other configuration.', array('@cache-settings' => \Drupal::url('system.performance_settings')))  . '</p>';
+      $output .= '</dd>';
+      $output .= '</dl>';
+
+      return $output;
+  }
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function page_cache_form_alter(&$form, FormStateInterface $form_state, $form_id) {
+  // If the page that's being built is cacheable, set the 'immutable' flag, to
+  // ensure that when the form is used, a new form build ID is generated when
+  // appropriate, to prevent information disclosure.
+  $request_policy = \Drupal::service('page_cache_request_policy');
+  $request = \Drupal::requestStack()->getCurrentRequest();
+  $request_is_cacheable = $request_policy->check($request) === RequestPolicyInterface::ALLOW;
+  if ($request_is_cacheable) {
+    $form_state->addBuildInfo('immutable', TRUE);
+  }
+}
diff --git a/core/modules/page_cache/page_cache.services.yml b/core/modules/page_cache/page_cache.services.yml
new file mode 100644
index 0000000..bbddfa7
--- /dev/null
+++ b/core/modules/page_cache/page_cache.services.yml
@@ -0,0 +1,6 @@
+services:
+  http_middleware.page_cache:
+    class: Drupal\page_cache\StackMiddleware\PageCache
+    arguments: ['@cache.render', '@page_cache_request_policy', '@page_cache_response_policy']
+    tags:
+      - { name: http_middleware, priority: 200 }
diff --git a/core/modules/page_cache/src/StackMiddleware/PageCache.php b/core/modules/page_cache/src/StackMiddleware/PageCache.php
new file mode 100644
index 0000000..b0dcce8
--- /dev/null
+++ b/core/modules/page_cache/src/StackMiddleware/PageCache.php
@@ -0,0 +1,304 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_cache\StackMiddleware\PageCache.
+ */
+
+namespace Drupal\page_cache\StackMiddleware;
+
+use Drupal\Component\Utility\UserAgent;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\PageCache\RequestPolicyInterface;
+use Drupal\Core\PageCache\ResponsePolicyInterface;
+use Drupal\Core\Site\Settings;
+use Symfony\Component\HttpFoundation\BinaryFileResponse;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\StreamedResponse;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+/**
+ * Executes the page caching before the main kernel takes over the request.
+ */
+class PageCache implements HttpKernelInterface {
+
+  /**
+   * The wrapped HTTP kernel.
+   *
+   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
+   */
+  protected $httpKernel;
+
+  /**
+   * The cache bin.
+   *
+   * @var \Drupal\Core\Cache\CacheBackendInterface.
+   */
+  protected $cache;
+
+  /**
+   * A policy rule determining the cacheability of a request.
+   *
+   * @var \Drupal\Core\PageCache\RequestPolicyInterface
+   */
+  protected $requestPolicy;
+
+  /**
+   * A policy rule determining the cacheability of the response.
+   *
+   * @var \Drupal\Core\PageCache\ResponsePolicyInterface
+   */
+  protected $responsePolicy;
+
+  /**
+   * Constructs a PageCache object.
+   *
+   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
+   *   The decorated kernel.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   The cache bin.
+   * @param \Drupal\Core\PageCache\RequestPolicyInterface $request_policy
+   *   A policy rule determining the cacheability of a request.
+   * @param \Drupal\Core\PageCache\ResponsePolicyInterface $response_policy
+   *   A policy rule determining the cacheability of the response.
+   */
+  public function __construct(HttpKernelInterface $http_kernel, CacheBackendInterface $cache, RequestPolicyInterface $request_policy, ResponsePolicyInterface $response_policy) {
+    $this->httpKernel = $http_kernel;
+    $this->cache = $cache;
+    $this->requestPolicy = $request_policy;
+    $this->responsePolicy = $response_policy;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
+    // Only allow page caching on master request.
+    if ($type === static::MASTER_REQUEST && $this->requestPolicy->check($request) === RequestPolicyInterface::ALLOW) {
+      $response = $this->lookup($request, $type, $catch);
+    }
+    else {
+      $response = $this->pass($request, $type, $catch);
+    }
+
+    return $response;
+  }
+
+  /**
+   * Sidesteps the page cache and directly forwards a request to the backend.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   * @param int $type
+   *   The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
+   *   HttpKernelInterface::SUB_REQUEST)
+   * @param bool $catch
+   *   Whether to catch exceptions or not
+   *
+   * @returns \Symfony\Component\HttpFoundation\Response $response
+   *   A response object.
+   */
+  protected function pass(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
+    return $this->httpKernel->handle($request, $type, $catch);
+  }
+
+  /**
+   * Retrieves a response from the cache or fetches it from the backend.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   * @param int $type
+   *   The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
+   *   HttpKernelInterface::SUB_REQUEST)
+   * @param bool $catch
+   *   Whether to catch exceptions or not
+   *
+   * @returns \Symfony\Component\HttpFoundation\Response $response
+   *   A response object.
+   */
+  protected function lookup(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
+    if ($response = $this->get($request)) {
+      $response->headers->set('X-Drupal-Cache', 'HIT');
+    }
+    else {
+      $response = $this->fetch($request, $type, $catch);
+    }
+
+    // Only allow caching in the browser and prevent that the response is stored
+    // by an external proxy server when the following conditions apply:
+    // 1. There is a session cookie on the request.
+    // 2. The Vary: Cookie header is on the response.
+    // 3. The Cache-Control header does not contain the no-cache directive.
+    if ($request->cookies->has(session_name()) &&
+      in_array('Cookie', $response->getVary()) &&
+      !$response->headers->hasCacheControlDirective('no-cache')) {
+
+      $response->setPrivate();
+    }
+
+    // Negotiate whether to use compression.
+    if (extension_loaded('zlib') && $response->headers->get('Content-Encoding') === 'gzip') {
+      if (strpos($request->headers->get('Accept-Encoding'), 'gzip') !== FALSE) {
+        // The response content is already gzip'ed, so make sure
+        // zlib.output_compression does not compress it once more.
+        ini_set('zlib.output_compression', '0');
+      }
+      else {
+        // The client does not support compression. Decompress the content and
+        // remove the Content-Encoding header.
+        $content = $response->getContent();
+        $content = gzinflate(substr(substr($content, 10), 0, -8));
+        $response->setContent($content);
+        $response->headers->remove('Content-Encoding');
+      }
+    }
+
+    // Perform HTTP revalidation.
+    // @todo Use Response::isNotModified() as per https://drupal.org/node/2259489
+    $last_modified = $response->getLastModified();
+    if ($last_modified) {
+      // See if the client has provided the required HTTP headers.
+      $if_modified_since = $request->server->has('HTTP_IF_MODIFIED_SINCE') ? strtotime($request->server->get('HTTP_IF_MODIFIED_SINCE')) : FALSE;
+      $if_none_match = $request->server->has('HTTP_IF_NONE_MATCH') ? stripslashes($request->server->get('HTTP_IF_NONE_MATCH')) : FALSE;
+
+      if ($if_modified_since && $if_none_match
+        && $if_none_match == $response->getEtag() // etag must match
+        && $if_modified_since == $last_modified->getTimestamp()) {  // if-modified-since must match
+        $response->setStatusCode(304);
+        $response->setContent(NULL);
+
+        // In the case of a 304 response, certain headers must be sent, and the
+        // remaining may not (see RFC 2616, section 10.3.5).
+        foreach (array_keys($response->headers->all()) as $name) {
+          if (!in_array($name, array('content-location', 'expires', 'cache-control', 'vary'))) {
+            $response->headers->remove($name);
+          }
+        }
+      }
+    }
+
+    return $response;
+  }
+
+  /**
+   * Fetches a response from the backend and stores it in the cache.
+   *
+   * If page_compression is enabled, a gzipped version of the page is stored in
+   * the cache to avoid compressing the output on each request. The cache entry
+   * is unzipped in the relatively rare event that the page is requested by a
+   * client without gzip support.
+   *
+   * Page compression requires the PHP zlib extension
+   * (http://php.net/manual/ref.zlib.php).
+   *
+   * @see drupal_page_header()
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   * @param int $type
+   *   The type of the request (one of HttpKernelInterface::MASTER_REQUEST or
+   *   HttpKernelInterface::SUB_REQUEST)
+   * @param bool $catch
+   *   Whether to catch exceptions or not
+   *
+   * @returns \Symfony\Component\HttpFoundation\Response $response
+   *   A response object.
+   */
+  protected function fetch(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
+    $response = $this->httpKernel->handle($request, $type, $catch);
+
+    // Currently it is not possible to cache some types of responses. Therefore
+    // exclude binary file responses (generated files, e.g. images with image
+    // styles) and streamed responses (files directly read from the disk).
+    // see: https://github.com/symfony/symfony/issues/9128#issuecomment-25088678
+    if ($response instanceof BinaryFileResponse || $response instanceof StreamedResponse) {
+      return $response;
+    }
+
+    if ($this->responsePolicy->check($response, $request) === ResponsePolicyInterface::DENY) {
+      return $response;
+    }
+
+    // Use the actual timestamp from an Expires header, if available.
+    $date = $response->getExpires();
+    $expire = ($date > (new \DateTime())) ? $date->getTimestamp() : Cache::PERMANENT;
+
+    $tags = explode(' ', $response->headers->get('X-Drupal-Cache-Tags'));
+    $this->set($request, $response, $expire, $tags);
+
+    // Mark response as a cache miss.
+    $response->headers->set('X-Drupal-Cache', 'MISS');
+
+    return $response;
+  }
+
+  /**
+   * Returns a response object from the page cache.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   * @param bool $allow_invalid
+   *   (optional) If TRUE, a cache item may be returned even if it is expired or
+   *   has been invalidated. Such items may sometimes be preferred, if the
+   *   alternative is recalculating the value stored in the cache, especially
+   *   if another concurrent request is already recalculating the same value.
+   *   The "valid" property of the returned object indicates whether the item is
+   *   valid or not. Defaults to FALSE.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response|false
+   *   The cached response or FALSE on failure.
+   */
+  protected function get(Request $request, $allow_invalid = FALSE) {
+    $cid = $this->getCacheId($request);
+    if ($cache = $this->cache->get($cid, $allow_invalid)) {
+      return $cache->data;
+    }
+  }
+
+  /**
+   * Stores a response object in the page cache.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   * @param \Symfony\Component\HttpFoundation\Response $response
+   *   The response to store in the cache.
+   * @param int $expire
+   *   One of the following values:
+   *   - CacheBackendInterface::CACHE_PERMANENT: Indicates that the item should
+   *     not be removed unless it is deleted explicitly.
+   *   - A Unix timestamp: Indicates that the item will be considered invalid
+   *     after this time, i.e. it will not be returned by get() unless
+   *     $allow_invalid has been set to TRUE. When the item has expired, it may
+   *     be permanently deleted by the garbage collector at any time.
+   * @param array $tags
+   *   An array of tags to be stored with the cache item. These should normally
+   *   identify objects used to build the cache item, which should trigger
+   *   cache invalidation when updated. For example if a cached item represents
+   *   a node, both the node ID and the author's user ID might be passed in as
+   *   tags. For example array('node' => array(123), 'user' => array(92)).
+   */
+  protected function set(Request $request, Response $response, $expire, array $tags) {
+    $cid = $this->getCacheId($request);
+    $this->cache->set($cid, $response, $expire, $tags);
+  }
+
+  /**
+   * Gets the page cache ID for this request.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   A request object.
+   *
+   * @return string
+   *   The cache ID for this request.
+   */
+  protected function getCacheId(Request $request) {
+    $cid_parts = array(
+      $request->getUri(),
+      $request->getRequestFormat(),
+    );
+    return implode(':', $cid_parts);
+  }
+
+}
diff --git a/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
new file mode 100644
index 0000000..e6a5d74
--- /dev/null
+++ b/core/modules/page_cache/src/Tests/PageCacheTagsIntegrationTest.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_cache\Tests\PageCacheTagsIntegrationTest.
+ */
+
+namespace Drupal\page_cache\Tests;
+
+use Drupal\simpletest\WebTestBase;
+use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
+
+/**
+ * Enables the page cache and tests its cache tags in various scenarios.
+ *
+ * @group Cache
+ * @see \Drupal\page_cache\Tests\PageCacheTest
+ * @see \Drupal\node\Tests\NodePageCacheTest
+ * @see \Drupal\menu_ui\Tests\MenuTest::testMenuBlockPageCacheTags()
+ */
+class PageCacheTagsIntegrationTest extends WebTestBase {
+
+  use AssertPageCacheContextsAndTagsTrait;
+
+  protected $profile = 'standard';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['page_cache'];
+
+  protected $dumpHeaders = TRUE;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->enablePageCaching();
+  }
+
+  /**
+   * Test that cache tags are properly bubbled up to the page level.
+   */
+  function testPageCacheTags() {
+    // Create two nodes.
+    $author_1 = $this->drupalCreateUser();
+    $node_1 = $this->drupalCreateNode(array(
+      'uid' => $author_1->id(),
+      'title' => 'Node 1',
+      'body' => array(
+        0 => array('value' => 'Body 1', 'format' => 'basic_html'),
+      ),
+      'promote' => NODE_PROMOTED,
+    ));
+    $author_2 = $this->drupalCreateUser();
+    $node_2 = $this->drupalCreateNode(array(
+      'uid' => $author_2->id(),
+      'title' => 'Node 2',
+      'body' => array(
+        0 => array('value' => 'Body 2', 'format' => 'full_html'),
+      ),
+      'promote' => NODE_PROMOTED,
+    ));
+
+    // Place a block, but only make it visible on full node page 2.
+    $block = $this->drupalPlaceBlock('views_block:comments_recent-block_1', array(
+      'visibility' => array(
+        'request_path' => array(
+          'pages' => 'node/' . $node_2->id(),
+        ),
+      ),
+    ));
+
+    $cache_contexts = [
+      'language',
+      'menu.active_trail:account',
+      'menu.active_trail:footer',
+      'menu.active_trail:main',
+      'menu.active_trail:tools',
+      'theme',
+      'timezone',
+      'user.roles',
+    ];
+
+    // Full node page 1.
+    $this->assertPageCacheContextsAndTags($node_1->urlInfo(), $cache_contexts, array(
+      'rendered',
+      'block_view',
+      'config:block_list',
+      'config:block.block.bartik_breadcrumbs',
+      'config:block.block.bartik_content',
+      'config:block.block.bartik_tools',
+      'config:block.block.bartik_login',
+      'config:block.block.bartik_footer',
+      'config:block.block.bartik_powered',
+      'config:block.block.bartik_main_menu',
+      'config:block.block.bartik_account_menu',
+      'config:block.block.bartik_messages',
+      'node_view',
+      'node:' . $node_1->id(),
+      'user:' . $author_1->id(),
+      'config:filter.format.basic_html',
+      'config:system.menu.account',
+      'config:system.menu.tools',
+      'config:system.menu.footer',
+      'config:system.menu.main',
+      'config:system.site',
+    ));
+
+    // Full node page 2.
+    $this->assertPageCacheContextsAndTags($node_2->urlInfo(), $cache_contexts, array(
+      'rendered',
+      'block_view',
+      'config:block_list',
+      'config:block.block.bartik_breadcrumbs',
+      'config:block.block.bartik_content',
+      'config:block.block.bartik_tools',
+      'config:block.block.bartik_login',
+      'config:block.block.' . $block->id(),
+      'config:block.block.bartik_footer',
+      'config:block.block.bartik_powered',
+      'config:block.block.bartik_main_menu',
+      'config:block.block.bartik_account_menu',
+      'config:block.block.bartik_messages',
+      'node_view',
+      'node:' . $node_2->id(),
+      'user:' . $author_2->id(),
+      'config:filter.format.full_html',
+      'config:system.menu.account',
+      'config:system.menu.tools',
+      'config:system.menu.footer',
+      'config:system.menu.main',
+      'config:system.site',
+      'comment_list',
+      'node_list',
+      'config:views.view.comments_recent',
+    ));
+  }
+
+}
diff --git a/core/modules/page_cache/src/Tests/PageCacheTest.php b/core/modules/page_cache/src/Tests/PageCacheTest.php
new file mode 100644
index 0000000..b21a4b1
--- /dev/null
+++ b/core/modules/page_cache/src/Tests/PageCacheTest.php
@@ -0,0 +1,272 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_cache\Tests\PageCacheTest.
+ */
+
+namespace Drupal\page_cache\Tests;
+
+use Drupal\Component\Datetime\DateTimePlus;
+use Drupal\Core\Form\FormState;
+use Drupal\Core\PageCache\ChainRequestPolicy;
+use Drupal\Core\PageCache\RequestPolicy\NoSessionOpen;
+use Drupal\Core\Routing\RequestContext;
+use Drupal\simpletest\WebTestBase;
+use Drupal\Core\Cache\Cache;
+use Drupal\user\Entity\Role;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Enables the page cache and tests it with various HTTP requests.
+ *
+ * @group page_cache
+ */
+class PageCacheTest extends WebTestBase {
+
+  protected $dumpHeaders = TRUE;
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('test_page_test', 'system_test', 'page_cache');
+
+  protected function setUp() {
+    parent::setUp();
+
+    $this->config('system.site')
+      ->set('name', 'Drupal')
+      ->set('page.front', 'test-page')
+      ->save();
+
+    $config = $this->config('system.performance');
+    $config->set('cache.page.max_age', 300);
+    $config->save();
+
+    // As the DefaultRequestPolicy deny cache for CLI requests,
+    // build request policy without CLI check.
+    $this->container->set('page_cache_request_policy', NULL);
+    /** @var  \Drupal\Core\Session\SessionConfigurationInterface $session_configuration */
+    $session_configuration = $this->container->get('session_configuration');
+    /** @var \Drupal\Core\PageCache\RequestPolicyInterface $request_policy */
+    $request_policy = new ChainRequestPolicy();
+    $request_policy->addPolicy(new NoSessionOpen($session_configuration));
+    $this->container->set('page_cache_request_policy', $request_policy);
+  }
+
+  /**
+   * Test that cache tags are properly persisted.
+   *
+   * Since tag based invalidation works, we know that our tag properly
+   * persisted.
+   */
+  function testPageCacheTags() {
+    $path = 'system-test/cache_tags_page';
+    $tags = array('system_test_cache_tags_page');
+    $this->drupalGet($path);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+
+    // Verify a cache hit, but also the presence of the correct cache tags.
+    $this->drupalGet($path);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
+    $cid_parts = array(\Drupal::url('system_test.cache_tags_page', array(), array('absolute' => TRUE)), 'html');
+    $cid = implode(':', $cid_parts);
+    $cache_entry = \Drupal::cache('render')->get($cid);
+    sort($cache_entry->tags);
+    $expected_tags = array(
+      'pre_render',
+      'rendered',
+      'system_test_cache_tags_page',
+    );
+    $this->assertIdentical($cache_entry->tags, $expected_tags);
+
+    Cache::invalidateTags($tags);
+    $this->drupalGet($path);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+  }
+
+  /**
+   * Tests support for different cache items with different Accept headers.
+   */
+  function testAcceptHeaderRequests() {
+    $url_generator = \Drupal::urlGenerator();
+    $url_generator->setContext(new RequestContext());
+    $accept_header_cache_uri = $url_generator->getPathFromRoute('system_test.page_cache_accept_header');
+    $json_accept_header = array('Accept: application/json');
+
+    $this->drupalGet($accept_header_cache_uri);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'HTML page was not yet cached.');
+    $this->drupalGet($accept_header_cache_uri);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'HTML page was cached.');
+    $this->assertRaw('<p>oh hai this is html.</p>', 'The correct HTML response was returned.');
+
+    $this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Json response was not yet cached.');
+    $this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Json response was cached.');
+    $this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
+
+    // Enable REST support for nodes and hal+json.
+    \Drupal::service('module_installer')->install(['node', 'rest', 'hal']);
+    $this->drupalCreateContentType(['type' => 'article']);
+    $node = $this->drupalCreateNode(['type' => 'article']);
+    $node_uri = 'node/' . $node->id();
+    $hal_json_accept_header = ['Accept: application/hal+json'];
+    /** @var \Drupal\user\RoleInterface $role */
+    $role = Role::load('anonymous');
+    $role->grantPermission('restful get entity:node');
+    $role->save();
+
+    $this->drupalGet($node_uri);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
+    $this->drupalGet($node_uri);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
+
+    // Now request a HAL page, we expect that the first request is a cache miss
+    // and it serves HTML.
+    $this->drupalGet($node_uri, [], $hal_json_accept_header);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
+    $this->drupalGet($node_uri, [], $hal_json_accept_header);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
+
+    // Clear the page cache. After that request a HAL request, followed by an
+    // ordinary HTML one.
+    \Drupal::cache('render')->deleteAll();
+    $this->drupalGet($node_uri, [], $hal_json_accept_header);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
+    $this->drupalGet($node_uri, [], $hal_json_accept_header);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
+
+    $this->drupalGet($node_uri);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
+    $this->drupalGet($node_uri);
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
+    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
+  }
+
+  /**
+   * Tests support of requests with If-Modified-Since and If-None-Match headers.
+   */
+  function testConditionalRequests() {
+    // Fill the cache.
+    $this->drupalGet('');
+    // Verify the page is not printed twice when the cache is cold.
+    $this->assertNoPattern('#<html.*<html#');
+
+    $this->drupalHead('');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+    $etag = $this->drupalGetHeader('ETag');
+    $last_modified = $this->drupalGetHeader('Last-Modified');
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
+    $this->assertResponse(304, 'Conditional request returned 304 Not Modified.');
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
+    $this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
+    $this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
+    // Verify the page is not printed twice when the cache is warm.
+    $this->assertNoPattern('#<html.*<html#');
+    $this->assertResponse(200, 'Conditional request without If-None-Match returned 200 OK.');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DateTimePlus::RFC7231, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
+    $this->assertResponse(200, 'Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
+    $this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.');
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absense of Page was not cached.');
+  }
+
+  /**
+   * Tests cache headers.
+   */
+  function testPageCache() {
+    // Fill the cache.
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+    // Symfony's Response logic determines a specific order for the subvalues
+    // of the Cache-Control header, even if they are explicitly passed in to
+    // the response header bag in a different order.
+    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.');
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+
+    // Check cache.
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.');
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+
+    // Check replacing default headers.
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', 'Default header was replaced.');
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
+
+    // Check that authenticated users bypass the cache.
+    $user = $this->drupalCreateUser();
+    $this->drupalLogin($user);
+    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
+    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
+    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent.');
+    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
+    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
+  }
+
+  /**
+   * Tests the omit_vary_cookie setting.
+   */
+  public function testPageCacheWithoutVaryCookie() {
+    $settings['settings']['omit_vary_cookie'] = (object) array(
+      'value' => TRUE,
+      'required' => TRUE,
+    );
+    $this->writeSettings($settings);
+
+    // Fill the cache.
+    $this->drupalGet('');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
+    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
+
+    // Check cache.
+    $this->drupalGet('');
+    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
+    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
+  }
+
+  /**
+   * Test the setting of forms to be immutable.
+   */
+  public function testFormImmutability() {
+    // Install the module that provides the test form.
+    $this->container->get('module_installer')
+      ->install(['page_cache_form_test']);
+
+    $this->drupalGet('page_cache_form_test_immutability');
+
+    $this->assertText("Immutable: TRUE", "Form is immutable.");
+
+    // Uninstall the page_cache module, verify the flag is not set.
+    $this->container->get('module_installer')->uninstall(['page_cache']);
+
+    $this->drupalGet('page_cache_form_test_immutability');
+
+    $this->assertText("Immutable: FALSE", "Form is not immutable,");
+  }
+}
diff --git a/core/modules/page_cache/tests/modules/page_cache_form_test.info.yml b/core/modules/page_cache/tests/modules/page_cache_form_test.info.yml
new file mode 100644
index 0000000..e18bb53
--- /dev/null
+++ b/core/modules/page_cache/tests/modules/page_cache_form_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Page Cache Form Test'
+type: module
+description: 'Support module for the Page Cache module tests.'
+core: 8.x
+package: Testing
+version: VERSION
diff --git a/core/modules/page_cache/tests/modules/page_cache_form_test.module b/core/modules/page_cache/tests/modules/page_cache_form_test.module
new file mode 100644
index 0000000..339811e
--- /dev/null
+++ b/core/modules/page_cache/tests/modules/page_cache_form_test.module
@@ -0,0 +1,20 @@
+<?php
+
+/**
+ * @file
+ * Provides functionality for testing form caching
+ */
+
+use Drupal\Core\Form\FormStateInterface;
+
+/**
+* Implements hook_form_FORM_ID_alter().
+*/
+function page_cache_form_test_form_page_cache_form_test_alter(&$form, FormStateInterface $form_state, $form_id) {
+  if (isset($form_state->getBuildInfo()['immutable']) && $form_state->getBuildInfo()['immutable']) {
+    $form['#suffix'] = 'Immutable: TRUE';
+  }
+  else {
+    $form['#suffix'] = 'Immutable: FALSE';
+  }
+}
diff --git a/core/modules/page_cache/tests/modules/page_cache_form_test.routing.yml b/core/modules/page_cache/tests/modules/page_cache_form_test.routing.yml
new file mode 100644
index 0000000..fce728d
--- /dev/null
+++ b/core/modules/page_cache/tests/modules/page_cache_form_test.routing.yml
@@ -0,0 +1,6 @@
+page_cache_form_test.test_immutability:
+  path: '/page_cache_form_test_immutability'
+  defaults:
+    _form: '\Drupal\page_cache_form_test\Form\TestForm'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/page_cache/tests/modules/src/Form/TestForm.php b/core/modules/page_cache/tests/modules/src/Form/TestForm.php
new file mode 100644
index 0000000..535f2b2
--- /dev/null
+++ b/core/modules/page_cache/tests/modules/src/Form/TestForm.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\page_cache_form_test\Form\TestForm.
+ */
+
+namespace Drupal\page_cache_form_test\Form;
+
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormStateInterface;
+
+class TestForm extends FormBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'page_cache_form_test';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form['#prefix'] = '<p>Llamas are awesome, but kittens are pretty cool too!</p>';
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) { }
+
+}
diff --git a/core/modules/system/config/install/system.performance.yml b/core/modules/system/config/install/system.performance.yml
index 1e75b4b..11392bd 100644
--- a/core/modules/system/config/install/system.performance.yml
+++ b/core/modules/system/config/install/system.performance.yml
@@ -1,6 +1,5 @@
 cache:
   page:
-    use_internal: false
     max_age: 0
 css:
   preprocess: true
@@ -13,6 +12,4 @@ fast_404:
 js:
   preprocess: true
   gzip: true
-response:
-  gzip: false
 stale_file_threshold: 2592000
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 6888a56..c108618 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -161,14 +161,11 @@ system.performance:
       mapping:
         page:
           type: mapping
-          label: 'Page cache'
+          label: 'Page caching'
           mapping:
-            use_internal:
-              type: boolean
-              label: 'Use internal page cache'
             max_age:
               type: integer
-              label: 'Max age of page cache'
+              label: 'Max age'
     css:
       type: mapping
       label: 'CSS performance settings'
diff --git a/core/modules/system/src/Form/PerformanceForm.php b/core/modules/system/src/Form/PerformanceForm.php
index 9049c78..c5eb48c 100644
--- a/core/modules/system/src/Form/PerformanceForm.php
+++ b/core/modules/system/src/Form/PerformanceForm.php
@@ -121,6 +121,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#type' => 'details',
       '#title' => t('Caching'),
       '#open' => TRUE,
+      '#description' => $this->t('Note: Drupal provides an internal page cache module that is recommended for small to medium-sized websites.'),
     );
     // Identical options to the ones for block caching.
     // @see \Drupal\Core\Block\BlockBase::buildConfigurationForm()
@@ -132,14 +133,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#title' => t('Page cache maximum age'),
       '#default_value' => $config->get('cache.page.max_age'),
       '#options' => $period,
-      '#description' => t('The maximum time a page can be cached. This is used as the value for max-age in Cache-Control headers.'),
-    );
-
-    $form['caching']['cache'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Use internal page cache'),
-      '#description' => t("If a reverse proxy cache isn't available, use Drupal's internal cache system to store cached pages."),
-      '#default_value' => $config->get('cache.page.use_internal'),
+      '#description' => t('The maximum time a page can be cached by browsers and proxies. This is used as the value for max-age in Cache-Control headers.'),
     );
 
     $directory = 'public://';
@@ -157,16 +151,6 @@ public function buildForm(array $form, FormStateInterface $form_state) {
       '#description' => t('External resources can be optimized automatically, which can reduce both the size and number of requests made to your website.') . $disabled_message,
     );
 
-    $form['bandwidth_optimization']['page_compression'] = array(
-      '#type' => 'checkbox',
-      '#title' => t('Compress cached pages'),
-      '#default_value' => $config->get('response.gzip'),
-      '#states' => array(
-        'visible' => array(
-          'input[name="cache"]' => array('checked' => TRUE),
-        ),
-      ),
-    );
     $form['bandwidth_optimization']['preprocess_css'] = array(
       '#type' => 'checkbox',
       '#title' => t('Aggregate CSS files'),
@@ -195,9 +179,7 @@ public function submitForm(array &$form, FormStateInterface $form_state) {
     $this->renderCache->deleteAll();
 
     $this->config('system.performance')
-      ->set('cache.page.use_internal', $form_state->getValue('cache'))
       ->set('cache.page.max_age', $form_state->getValue('page_cache_maximum_age'))
-      ->set('response.gzip', $form_state->getValue('page_compression'))
       ->set('css.preprocess', $form_state->getValue('preprocess_css'))
       ->set('js.preprocess', $form_state->getValue('preprocess_js'))
       ->save();
diff --git a/core/modules/system/src/Tests/Ajax/AjaxFormPageCacheTest.php b/core/modules/system/src/Tests/Ajax/AjaxFormPageCacheTest.php
index 9da0935..3e36515 100644
--- a/core/modules/system/src/Tests/Ajax/AjaxFormPageCacheTest.php
+++ b/core/modules/system/src/Tests/Ajax/AjaxFormPageCacheTest.php
@@ -17,11 +17,15 @@ class AjaxFormPageCacheTest extends AjaxTestBase {
   /**
    * {@inheritdoc}
    */
+  public static $modules = ['page_cache'];
+
+  /**
+   * {@inheritdoc}
+   */
   public 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();
   }
diff --git a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php b/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
deleted file mode 100644
index c389a2a..0000000
--- a/core/modules/system/src/Tests/Bootstrap/PageCacheTest.php
+++ /dev/null
@@ -1,312 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Bootstrap\PageCacheTest.
- */
-
-namespace Drupal\system\Tests\Bootstrap;
-
-use Drupal\Component\Datetime\DateTimePlus;
-use Drupal\Core\Routing\RequestContext;
-use Drupal\simpletest\WebTestBase;
-use Drupal\Core\Cache\Cache;
-use Drupal\user\Entity\Role;
-
-/**
- * Enables the page cache and tests it with various HTTP requests.
- *
- * @group Bootstrap
- */
-class PageCacheTest extends WebTestBase {
-
-  protected $dumpHeaders = TRUE;
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('test_page_test', 'system_test');
-
-  protected function setUp() {
-    parent::setUp();
-
-    $this->config('system.site')
-      ->set('name', 'Drupal')
-      ->set('page.front', 'test-page')
-      ->save();
-  }
-
-  /**
-   * Test that cache tags are properly persisted.
-   *
-   * Since tag based invalidation works, we know that our tag properly
-   * persisted.
-   */
-  function testPageCacheTags() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->save();
-
-    $path = 'system-test/cache_tags_page';
-    $tags = array('system_test_cache_tags_page');
-    $this->drupalGet($path);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-
-    // Verify a cache hit, but also the presence of the correct cache tags.
-    $this->drupalGet($path);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
-    $cid_parts = array(\Drupal::url('system_test.cache_tags_page', array(), array('absolute' => TRUE)), 'html');
-    $cid = implode(':', $cid_parts);
-    $cache_entry = \Drupal::cache('render')->get($cid);
-    sort($cache_entry->tags);
-    $expected_tags = array(
-      'pre_render',
-      'rendered',
-      'system_test_cache_tags_page',
-    );
-    $this->assertIdentical($cache_entry->tags, $expected_tags);
-
-    Cache::invalidateTags($tags);
-    $this->drupalGet($path);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-  }
-
-  /**
-   * Tests support for different cache items with different Accept headers.
-   */
-  function testAcceptHeaderRequests() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->save();
-
-    $url_generator = \Drupal::urlGenerator();
-    $url_generator->setContext(new RequestContext());
-    $accept_header_cache_uri = $url_generator->getPathFromRoute('system_test.page_cache_accept_header');
-    $json_accept_header = array('Accept: application/json');
-
-    $this->drupalGet($accept_header_cache_uri);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'HTML page was not yet cached.');
-    $this->drupalGet($accept_header_cache_uri);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'HTML page was cached.');
-    $this->assertRaw('<p>oh hai this is html.</p>', 'The correct HTML response was returned.');
-
-    $this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Json response was not yet cached.');
-    $this->drupalGet($accept_header_cache_uri, array(), $json_accept_header);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Json response was cached.');
-    $this->assertRaw('{"content":"oh hai this is json"}', 'The correct Json response was returned.');
-
-    // Enable REST support for nodes and hal+json.
-    \Drupal::service('module_installer')->install(['node', 'rest', 'hal']);
-    $this->drupalCreateContentType(['type' => 'article']);
-    $node = $this->drupalCreateNode(['type' => 'article']);
-    $node_uri = 'node/' . $node->id();
-    $hal_json_accept_header = ['Accept: application/hal+json'];
-    /** @var \Drupal\user\RoleInterface $role */
-    $role = Role::load('anonymous');
-    $role->grantPermission('restful get entity:node');
-    $role->save();
-
-    $this->drupalGet($node_uri);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
-    $this->drupalGet($node_uri);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
-
-    // Now request a HAL page, we expect that the first request is a cache miss
-    // and it serves HTML.
-    $this->drupalGet($node_uri, [], $hal_json_accept_header);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
-    $this->drupalGet($node_uri, [], $hal_json_accept_header);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
-
-    // Clear the page cache. After that request a HAL request, followed by an
-    // ordinary HTML one.
-    \Drupal::cache('render')->deleteAll();
-    $this->drupalGet($node_uri, [], $hal_json_accept_header);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
-    $this->drupalGet($node_uri, [], $hal_json_accept_header);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'application/hal+json');
-
-    $this->drupalGet($node_uri);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
-    $this->drupalGet($node_uri);
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT');
-    $this->assertEqual($this->drupalGetHeader('Content-Type'), 'text/html; charset=UTF-8');
-  }
-
-  /**
-   * Tests support of requests with If-Modified-Since and If-None-Match headers.
-   */
-  function testConditionalRequests() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->save();
-
-    // Fill the cache.
-    $this->drupalGet('');
-    // Verify the page is not printed twice when the cache is cold.
-    $this->assertNoPattern('#<html.*<html#');
-
-    $this->drupalHead('');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-    $etag = $this->drupalGetHeader('ETag');
-    $last_modified = $this->drupalGetHeader('Last-Modified');
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
-    $this->assertResponse(304, 'Conditional request returned 304 Not Modified.');
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC822, strtotime($last_modified)), 'If-None-Match: ' . $etag));
-    $this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DATE_RFC850, strtotime($last_modified)), 'If-None-Match: ' . $etag));
-    $this->assertResponse(304, 'Conditional request with obsolete If-Modified-Since date returned 304 Not Modified.');
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified));
-    // Verify the page is not printed twice when the cache is warm.
-    $this->assertNoPattern('#<html.*<html#');
-    $this->assertResponse(200, 'Conditional request without If-None-Match returned 200 OK.');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . gmdate(DateTimePlus::RFC7231, strtotime($last_modified) + 1), 'If-None-Match: ' . $etag));
-    $this->assertResponse(200, 'Conditional request with new a If-Modified-Since date newer than Last-Modified returned 200 OK.');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-
-    $user = $this->drupalCreateUser();
-    $this->drupalLogin($user);
-    $this->drupalGet('', array(), array('If-Modified-Since: ' . $last_modified, 'If-None-Match: ' . $etag));
-    $this->assertResponse(200, 'Conditional request returned 200 OK for authenticated user.');
-    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Absense of Page was not cached.');
-  }
-
-  /**
-   * Tests cache headers.
-   */
-  function testPageCache() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->set('response.gzip', 1);
-    $config->save();
-
-    // Fill the cache.
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
-    $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'cookie,accept-encoding', 'Vary header was sent.');
-    // Symfony's Response logic determines a specific order for the subvalues
-    // of the Cache-Control header, even if they are explicitly passed in to
-    // the response header bag in a different order.
-    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
-
-    // Check cache.
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-    $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'cookie,accept-encoding', 'Vary: Cookie header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'max-age=300, public', 'Cache-Control header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
-
-    // Check replacing default headers.
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Expires', 'value' => 'Fri, 19 Nov 2008 05:00:00 GMT')));
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Fri, 19 Nov 2008 05:00:00 GMT', 'Default header was replaced.');
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Vary', 'value' => 'User-Agent')));
-    $this->assertEqual(strtolower($this->drupalGetHeader('Vary')), 'user-agent,accept-encoding', 'Default header was replaced.');
-
-    // Check that authenticated users bypass the cache.
-    $user = $this->drupalCreateUser();
-    $this->drupalLogin($user);
-    $this->drupalGet('system-test/set-header', array('query' => array('name' => 'Foo', 'value' => 'bar')));
-    $this->assertFalse($this->drupalGetHeader('X-Drupal-Cache'), 'Caching was bypassed.');
-    $this->assertTrue(strpos(strtolower($this->drupalGetHeader('Vary')), 'cookie') === FALSE, 'Vary: Cookie header was not sent.');
-    $this->assertEqual($this->drupalGetHeader('Cache-Control'), 'must-revalidate, no-cache, post-check=0, pre-check=0, private', 'Cache-Control header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Expires'), 'Sun, 19 Nov 1978 05:00:00 GMT', 'Expires header was sent.');
-    $this->assertEqual($this->drupalGetHeader('Foo'), 'bar', 'Custom header was sent.');
-  }
-
-  /**
-   * Tests the omit_vary_cookie setting.
-   */
-  public function testPageCacheWithoutVaryCookie() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->save();
-
-    $settings['settings']['omit_vary_cookie'] = (object) array(
-      'value' => TRUE,
-      'required' => TRUE,
-    );
-    $this->writeSettings($settings);
-
-    // Fill the cache.
-    $this->drupalGet('');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
-    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
-
-    // Check cache.
-    $this->drupalGet('');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-    $this->assertTrue(strpos($this->drupalGetHeader('Vary'), 'Cookie') === FALSE, 'Vary: Cookie header was not sent.');
-  }
-
-  /**
-   * Tests page compression.
-   *
-   * The test should pass even if zlib.output_compression is enabled in php.ini,
-   * .htaccess or similar, or if compression is done outside PHP, e.g. by the
-   * mod_deflate Apache module.
-   */
-  function testPageCompression() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->set('response.gzip', 1);
-    $config->save();
-
-    // Fill the cache and verify that output is compressed.
-    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'MISS', 'Page was not cached.');
-    $this->setRawContent(gzinflate(substr($this->getRawContent(), 10, -8)));
-    $this->assertRaw('</html>', 'Page was gzip compressed.');
-
-    // Verify that cached output is compressed.
-    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-    $this->assertEqual($this->drupalGetHeader('Content-Encoding'), 'gzip', 'A Content-Encoding header was sent.');
-    $this->setRawContent(gzinflate(substr($this->getRawContent(), 10, -8)));
-    $this->assertRaw('</html>', 'Page was gzip compressed.');
-
-    // Verify that a client without compression support gets an uncompressed page.
-    $this->drupalGet('');
-    $this->assertEqual($this->drupalGetHeader('X-Drupal-Cache'), 'HIT', 'Page was cached.');
-    $this->assertFalse($this->drupalGetHeader('Content-Encoding'), 'A Content-Encoding header was not sent.');
-    $this->assertTitle(t('Test page | @site-name', array('@site-name' => $this->config('system.site')->get('name'))), 'Site title matches.');
-    $this->assertRaw('</html>', 'Page was not compressed.');
-
-    // Disable compression mode.
-    $config->set('response.gzip', 0);
-    $config->save();
-
-    // Verify if cached page is still available for a client with compression support.
-    $this->drupalGet('', array(), array('Accept-Encoding: gzip,deflate'));
-    $this->setRawContent(gzinflate(substr($this->getRawContent(), 10, -8)));
-    $this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support enabled).');
-
-    // Verify if cached page is still available for a client without compression support.
-    $this->drupalGet('');
-    $this->assertRaw('</html>', 'Page was delivered after compression mode is changed (compression support disabled).');
-  }
-}
diff --git a/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php b/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php
index 1523421..0b4a205 100644
--- a/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php
+++ b/core/modules/system/src/Tests/Cache/AssertPageCacheContextsAndTagsTrait.php
@@ -20,8 +20,8 @@
    * Enables page caching.
    */
   protected function enablePageCaching() {
+    \Drupal::service('module_installer')->install(['page_cache']);
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
   }
diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php b/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
deleted file mode 100644
index f4d30ec..0000000
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsIntegrationTest.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\system\Tests\Cache\PageCacheTagsIntegrationTest.
- */
-
-namespace Drupal\system\Tests\Cache;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Enables the page cache and tests its cache tags in various scenarios.
- *
- * @group Cache
- * @see \Drupal\system\Tests\Bootstrap\PageCacheTest
- * @see \Drupal\node\Tests\NodePageCacheTest
- * @see \Drupal\menu_ui\Tests\MenuTest::testMenuBlockPageCacheTags()
- */
-class PageCacheTagsIntegrationTest extends WebTestBase {
-
-  use AssertPageCacheContextsAndTagsTrait;
-
-  protected $profile = 'standard';
-
-  protected $dumpHeaders = TRUE;
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUp() {
-    parent::setUp();
-
-    $this->enablePageCaching();
-  }
-
-  /**
-   * Test that cache tags are properly bubbled up to the page level.
-   */
-  function testPageCacheTags() {
-    // Create two nodes.
-    $author_1 = $this->drupalCreateUser();
-    $node_1 = $this->drupalCreateNode(array(
-      'uid' => $author_1->id(),
-      'title' => 'Node 1',
-      'body' => array(
-        0 => array('value' => 'Body 1', 'format' => 'basic_html'),
-      ),
-      'promote' => NODE_PROMOTED,
-    ));
-    $author_2 = $this->drupalCreateUser();
-    $node_2 = $this->drupalCreateNode(array(
-      'uid' => $author_2->id(),
-      'title' => 'Node 2',
-      'body' => array(
-        0 => array('value' => 'Body 2', 'format' => 'full_html'),
-      ),
-      'promote' => NODE_PROMOTED,
-    ));
-
-    // Place a block, but only make it visible on full node page 2.
-    $block = $this->drupalPlaceBlock('views_block:comments_recent-block_1', array(
-      'visibility' => array(
-        'request_path' => array(
-          'pages' => 'node/' . $node_2->id(),
-        ),
-      ),
-    ));
-
-    $cache_contexts = [
-      'language',
-      'menu.active_trail:account',
-      'menu.active_trail:footer',
-      'menu.active_trail:main',
-      'menu.active_trail:tools',
-      'theme',
-      'timezone',
-      'user.roles',
-    ];
-
-    // Full node page 1.
-    $this->assertPageCacheContextsAndTags($node_1->urlInfo(), $cache_contexts, array(
-      'rendered',
-      'block_view',
-      'config:block_list',
-      'config:block.block.bartik_breadcrumbs',
-      'config:block.block.bartik_content',
-      'config:block.block.bartik_tools',
-      'config:block.block.bartik_login',
-      'config:block.block.bartik_footer',
-      'config:block.block.bartik_powered',
-      'config:block.block.bartik_main_menu',
-      'config:block.block.bartik_account_menu',
-      'config:block.block.bartik_messages',
-      'node_view',
-      'node:' . $node_1->id(),
-      'user:' . $author_1->id(),
-      'config:filter.format.basic_html',
-      'config:system.menu.account',
-      'config:system.menu.tools',
-      'config:system.menu.footer',
-      'config:system.menu.main',
-      'config:system.site',
-    ));
-
-    // Full node page 2.
-    $this->assertPageCacheContextsAndTags($node_2->urlInfo(), $cache_contexts, array(
-      'rendered',
-      'block_view',
-      'config:block_list',
-      'config:block.block.bartik_breadcrumbs',
-      'config:block.block.bartik_content',
-      'config:block.block.bartik_tools',
-      'config:block.block.bartik_login',
-      'config:block.block.' . $block->id(),
-      'config:block.block.bartik_footer',
-      'config:block.block.bartik_powered',
-      'config:block.block.bartik_main_menu',
-      'config:block.block.bartik_account_menu',
-      'config:block.block.bartik_messages',
-      'node_view',
-      'node:' . $node_2->id(),
-      'user:' . $author_2->id(),
-      'config:filter.format.full_html',
-      'config:system.menu.account',
-      'config:system.menu.tools',
-      'config:system.menu.footer',
-      'config:system.menu.main',
-      'config:system.site',
-      'comment_list',
-      'node_list',
-      'config:views.view.comments_recent',
-    ));
-  }
-
-}
diff --git a/core/modules/system/src/Tests/Cache/PageCacheTagsTestBase.php b/core/modules/system/src/Tests/Cache/PageCacheTagsTestBase.php
index 3e3b1bc..7909300 100644
--- a/core/modules/system/src/Tests/Cache/PageCacheTagsTestBase.php
+++ b/core/modules/system/src/Tests/Cache/PageCacheTagsTestBase.php
@@ -26,12 +26,16 @@
   /**
    * {@inheritdoc}
    */
+  protected static $modules = ['page_cache'];
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
     parent::setUp();
 
     // Enable page caching.
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 3600);
     $config->save();
   }
diff --git a/core/modules/system/src/Tests/Form/FormStoragePageCacheTest.php b/core/modules/system/src/Tests/Form/FormStoragePageCacheTest.php
index d04be90..bac12db 100644
--- a/core/modules/system/src/Tests/Form/FormStoragePageCacheTest.php
+++ b/core/modules/system/src/Tests/Form/FormStoragePageCacheTest.php
@@ -19,7 +19,7 @@ class FormStoragePageCacheTest extends WebTestBase {
   /**
    * @var array
    */
-  public static $modules = array('form_test');
+  public static $modules = array('form_test', 'page_cache');
 
   /**
    * {@inheritdoc}
@@ -28,7 +28,6 @@ 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();
   }
diff --git a/core/modules/system/src/Tests/Session/SessionTest.php b/core/modules/system/src/Tests/Session/SessionTest.php
index 0921f66..ffd17aa 100644
--- a/core/modules/system/src/Tests/Session/SessionTest.php
+++ b/core/modules/system/src/Tests/Session/SessionTest.php
@@ -147,8 +147,8 @@ function testEmptyAnonymousSession() {
     $this->assertSessionEmpty(TRUE);
 
     // The same behavior is expected when caching is enabled.
+    $this->container->get('module_installer')->install(['page_cache']);
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
     $this->drupalGet('');
diff --git a/core/modules/system/src/Tests/System/ErrorHandlerTest.php b/core/modules/system/src/Tests/System/ErrorHandlerTest.php
index 9d1437f..07bb0be 100644
--- a/core/modules/system/src/Tests/System/ErrorHandlerTest.php
+++ b/core/modules/system/src/Tests/System/ErrorHandlerTest.php
@@ -155,8 +155,8 @@ function testExceptionHandler() {
 
     // Enable the page cache and disable error reporting, ensure that 5xx
     // responses are not cached.
+    \Drupal::service('module_installer')->install(['page_cache']);
     $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
     $config->set('cache.page.max_age', 300);
     $config->save();
     $this->config('system.logging')
diff --git a/core/modules/system/src/Tests/System/SiteMaintenanceTest.php b/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
index 4b42674..4890975 100644
--- a/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
+++ b/core/modules/system/src/Tests/System/SiteMaintenanceTest.php
@@ -42,9 +42,6 @@ protected function setUp() {
    * Verify site maintenance mode functionality with page cache disabled.
    */
   function testSiteMaintenanceWithoutPageCache() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 0);
-    $config->save();
     $this->doTestSiteMaintenance();
   }
 
@@ -52,10 +49,6 @@ function testSiteMaintenanceWithoutPageCache() {
    * Verify site maintenance mode functionality with page cache enabled.
    */
   function testSiteMaintenanceWithPageCache() {
-    $config = $this->config('system.performance');
-    $config->set('cache.page.use_internal', 1);
-    $config->set('cache.page.max_age', 300);
-    $config->save();
     $this->doTestSiteMaintenance();
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php b/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php
index 138309e..4f145dc 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormCacheTest.php
@@ -75,13 +75,6 @@ class FormCacheTest extends UnitTestCase {
   protected $logger;
 
   /**
-   * The config factory.
-   *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
-   */
-  protected $configFactory;
-
-  /**
    * The request stack.
    *
    * @var \Symfony\Component\HttpFoundation\RequestStack|\PHPUnit_Framework_MockObject_MockObject
@@ -121,11 +114,10 @@ protected function setUp() {
     $this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
 
     $this->logger = $this->getMock('Psr\Log\LoggerInterface');
-    $this->configFactory = $this->getConfigFactoryStub(['system.performance' => ['cache.page.use_internal' => FALSE]]);
     $this->requestStack = $this->getMock('\Symfony\Component\HttpFoundation\RequestStack');
     $this->requestPolicy = $this->getMock('\Drupal\Core\PageCache\RequestPolicyInterface');
 
-    $this->formCache = new FormCache($this->root, $this->keyValueExpirableFactory, $this->moduleHandler, $this->account, $this->csrfToken, $this->logger, $this->configFactory, $this->requestStack, $this->requestPolicy);
+    $this->formCache = new FormCache($this->root, $this->keyValueExpirableFactory, $this->moduleHandler, $this->account, $this->csrfToken, $this->logger, $this->requestStack, $this->requestPolicy);
   }
 
   /**
@@ -487,43 +479,6 @@ public function testSetCacheBuildIdMismatch() {
   }
 
   /**
-   * @covers ::setCache
-   */
-  public function testSetCacheImmutableForm() {
-    $form_build_id = 'the_form_build_id';
-    $form = [
-      '#form_id' => 'the_form_id',
-    ];
-    $form_state = new FormState();
-
-    $this->formCacheStore->expects($this->once())
-      ->method('setWithExpire')
-      ->with($form_build_id, $form, $this->isType('int'));
-    $form_state_data = $form_state->getCacheableArray();
-    $form_state_data['build_info']['safe_strings'] = [];
-    // Ensure that the form is marked immutable.
-    $form_state_data['build_info']['immutable'] = TRUE;
-    $this->formStateCacheStore->expects($this->once())
-      ->method('setWithExpire')
-      ->with($form_build_id, $form_state_data, $this->isType('int'));
-
-    // Rebuild the FormCache with a config factory that will return a config
-    // object with the internal page cache enabled.
-    $this->configFactory = $this->getConfigFactoryStub(['system.performance' => ['cache.page.use_internal' => TRUE]]);
-    $root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
-    $this->formCache = $this->getMockBuilder('Drupal\Core\Form\FormCache')
-      ->setConstructorArgs([$root, $this->keyValueExpirableFactory, $this->moduleHandler, $this->account, $this->csrfToken, $this->logger, $this->configFactory, $this->requestStack, $this->requestPolicy])
-      ->setMethods(['isPageCacheable'])
-      ->getMock();
-
-    $this->formCache->expects($this->once())
-      ->method('isPageCacheable')
-      ->willReturn(TRUE);
-
-    $this->formCache->setCache($form_build_id, $form, $form_state);
-  }
-
-  /**
    * Ensures SafeMarkup does not bleed from one test to another.
    */
   protected function resetSafeMarkup() {
