diff --git a/core/authorize.php b/core/authorize.php
index d033b7e..dbab02f 100644
--- a/core/authorize.php
+++ b/core/authorize.php
@@ -51,7 +51,6 @@
  *   TRUE if the current user can run authorize.php, and FALSE if not.
  */
 function authorize_access_allowed() {
-  \Drupal::service('session_manager')->start();
   return Settings::get('allow_authorize_operations', TRUE) && \Drupal::currentUser()->hasPermission('administer software updates');
 }
 
diff --git a/core/core.services.yml b/core/core.services.yml
index 9291712..aadc0ed 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -438,6 +438,11 @@ services:
     arguments: ['@kernel']
     tags:
       - { name: http_middleware, priority: 100 }
+  http_middleware.session:
+    class: Drupal\Core\StackMiddleware\Session
+    arguments: ['@session']
+    tags:
+      - { name: http_middleware, priority: 50 }
   language_manager:
     class: Drupal\Core\Language\LanguageManager
     arguments: ['@language.default']
@@ -937,7 +942,7 @@ services:
     arguments: ['@module_handler', '@cache.discovery', '@language_manager']
   batch.storage:
     class: Drupal\Core\Batch\BatchStorage
-    arguments: ['@database', '@session_manager', '@csrf_token']
+    arguments: ['@database', '@session', '@csrf_token']
     tags:
       - { name: backend_overridable }
   replica_database_ignore__subscriber:
@@ -1030,6 +1035,20 @@ services:
   current_user:
     class: Drupal\Core\Session\AccountProxy
     arguments: ['@authentication', '@request_stack']
+  session:
+    class: Symfony\Component\HttpFoundation\Session\Session
+    arguments: ['@session_manager', '@session.attribute_bag', '@session.flash_bag']
+  session.flash_bag:
+    class: Symfony\Component\HttpFoundation\Session\Flash\FlashBag
+    public: false
+  session.attribute_bag:
+    class: Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag
+    public: false
+  session.subrequest_session_subscrber:
+    class: Drupal\Core\EventSubscriber\SubRequestSessionSubscriber
+    public: false
+    tags:
+      - { name: event_subscriber }
   session_manager:
     class: Drupal\Core\Session\SessionManager
     arguments: ['@request_stack', '@database', '@session_manager.metadata_bag', '@settings']
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index d2c1312..f75820e 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -19,9 +19,11 @@
 use Drupal\Core\Extension\ExtensionDiscovery;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\language\Entity\ConfigurableLanguage;
+use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\DependencyInjection\Reference;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Route;
 
 use GuzzleHttp\Exception\RequestException;
 
@@ -390,7 +392,6 @@ function install_begin_request($class_loader, &$install_state) {
   $kernel->setSitePath($site_path);
   $kernel->boot();
   $container = $kernel->getContainer();
-  $container->get('request_stack')->push($request);
 
   // Register the file translation service.
   if (isset($GLOBALS['config']['locale.settings']['translation']['path'])) {
@@ -436,13 +437,12 @@ function install_begin_request($class_loader, &$install_state) {
   if ($profile && !$module_handler->moduleExists($profile)) {
     $module_handler->addProfile($profile, $install_state['profiles'][$profile]->getPath());
   }
-  // After setting up a custom and finite module list in a custom low-level
-  // bootstrap like here, ensure to use ModuleHandler::loadAll() so that
-  // ModuleHandler::isLoaded() returns TRUE, since that is a condition being
-  // checked by other subsystems (e.g., the theme system).
-  $module_handler->loadAll();
 
-  $kernel->prepareLegacyRequest($request);
+  // Load all modules and perform request related initialization.
+  // @see \Drupal\Core\DrupalKernel::prepareLegacyRequest
+  $kernel->preHandle($request);
+  $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
+  $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
 
   // Prepare for themed output. We need to run this at the beginning of the
   // page request to avoid a different theme accidentally getting set. (We also
@@ -581,7 +581,7 @@ function install_run_task($task, &$install_state) {
       $response = batch_process(preg_replace('@^core/@', 'base://', install_redirect_url($install_state)), install_full_redirect_url($install_state));
       if ($response instanceof Response) {
         // Save $_SESSION data from batch.
-        \Drupal::service('session_manager')->save();
+        \Drupal::service('session')->save();
         // Send the response.
         $response->send();
         exit;
@@ -1542,7 +1542,7 @@ function install_load_profile(&$install_state) {
  *   An array of information about the current installation state.
  */
 function install_bootstrap_full() {
-  \Drupal::service('session_manager')->start();
+  \Drupal::service('session')->start();
 }
 
 /**
diff --git a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
index 5f88c47..e36275f 100644
--- a/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
+++ b/core/lib/Drupal/Core/Authentication/Provider/Cookie.php
@@ -18,27 +18,10 @@
 class Cookie implements AuthenticationProviderInterface {
 
   /**
-   * The session manager.
-   *
-   * @var \Drupal\Core\Session\SessionManagerInterface
-   */
-  protected $sessionManager;
-
-  /**
-   * Constructs a new Cookie authentication provider instance.
-   *
-   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
-   *   The session manager.
-   */
-  public function __construct(SessionManagerInterface $session_manager) {
-    $this->sessionManager = $session_manager;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function applies(Request $request) {
-    return TRUE;
+    return $request->hasSession();
   }
 
   /**
@@ -47,10 +30,11 @@ public function applies(Request $request) {
   public function authenticate(Request $request) {
     // Global $user is deprecated, but the session system is still based on it.
     global $user;
-    $this->sessionManager->start();
-    if ($this->sessionManager->isStarted()) {
+
+    if ($request->getSession()->start()) {
       return $user;
     }
+
     return NULL;
   }
 
@@ -58,7 +42,6 @@ public function authenticate(Request $request) {
    * {@inheritdoc}
    */
   public function cleanup(Request $request) {
-    $this->sessionManager->save();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Batch/BatchStorage.php b/core/lib/Drupal/Core/Batch/BatchStorage.php
index da0fe94..5742fd2 100644
--- a/core/lib/Drupal/Core/Batch/BatchStorage.php
+++ b/core/lib/Drupal/Core/Batch/BatchStorage.php
@@ -8,7 +8,7 @@
 namespace Drupal\Core\Batch;
 
 use Drupal\Core\Database\Connection;
-use Drupal\Core\Session\SessionManager;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
 use Drupal\Core\Access\CsrfTokenGenerator;
 
 class BatchStorage implements BatchStorageInterface {
@@ -21,11 +21,11 @@ class BatchStorage implements BatchStorageInterface {
   protected $connection;
 
   /**
-   * The session manager.
+   * The session.
    *
-   * @var \Drupal\Core\Session\SessionManager
+   * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
    */
-  protected $sessionManager;
+  protected $session;
 
   /**
    * The CSRF token generator.
@@ -39,14 +39,14 @@ class BatchStorage implements BatchStorageInterface {
    *
    * @param \Drupal\Core\Database\Connection $connection
    *   The database connection.
-   * @param \Drupal\Core\Session\SessionManager $session_manager
-   *   The session manager.
+   * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
+   *   The session.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token generator.
    */
-  public function __construct(Connection $connection, SessionManager $session_manager, CsrfTokenGenerator $csrf_token) {
+  public function __construct(Connection $connection, SessionInterface $session, CsrfTokenGenerator $csrf_token) {
     $this->connection = $connection;
-    $this->sessionManager = $session_manager;
+    $this->session = $session;
     $this->csrfToken = $csrf_token;
   }
 
@@ -55,7 +55,7 @@ public function __construct(Connection $connection, SessionManager $session_mana
    */
   public function load($id) {
     // Ensure that a session is started before using the CSRF token generator.
-    $this->sessionManager->start();
+    $this->session->start();
     $batch = $this->connection->query("SELECT batch FROM {batch} WHERE bid = :bid AND token = :token", array(
       ':bid' => $id,
       ':token' => $this->csrfToken->get($id),
@@ -100,7 +100,7 @@ public function cleanup() {
    */
   public function create(array $batch) {
     // Ensure that a session is started before using the CSRF token generator.
-    $this->sessionManager->start();
+    $this->session->start();
     $this->connection->insert('batch')
       ->fields(array(
         'bid' => $batch['id'],
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index d5e6059..4caaa93 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -590,8 +590,9 @@ public function handle(Request $request, $type = self::MASTER_REQUEST, $catch =
   public function prepareLegacyRequest(Request $request) {
     $this->boot();
     $this->preHandle($request);
-    // Enter the request scope so that current_user service is available for
-    // locale/translation sake.
+    // Setup services which are normally initialized from within stack
+    // middleware or during the request kernel event.
+    $request->setSession($this->container->get('session'));
     $request->attributes->set(RouteObjectInterface::ROUTE_OBJECT, new Route('<none>'));
     $request->attributes->set(RouteObjectInterface::ROUTE_NAME, '<none>');
     $this->container->get('request_stack')->push($request);
@@ -730,6 +731,16 @@ protected function initializeContainer($rebuild = FALSE) {
     if ($session_manager_started) {
       $this->container->get('session_manager')->start();
     }
+
+    // The request stack is preserved accross container rebuilds. Reinject the
+    // new session into the master request if one was present before.
+    if (($request_stack = $this->container->get('request_stack', ContainerInterface::NULL_ON_INVALID_REFERENCE))) {
+      if ($request = $request_stack->getMasterRequest()) {
+        if ($request->hasSession()) {
+          $request->setSession($this->container->get('session'));
+        }
+      }
+    }
     \Drupal::setContainer($this->container);
 
     // If needs dumping flag was set, dump the container.
diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
index 9535abf..389cc19 100644
--- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
@@ -56,7 +56,7 @@ protected static function getPriority() {
   }
 
   /**
-   * {@inheritDoc}
+   * {@inheritdoc}
    */
   protected function getHandledFormats() {
     return ['html'];
diff --git a/core/lib/Drupal/Core/EventSubscriber/SubRequestSessionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/SubRequestSessionSubscriber.php
new file mode 100644
index 0000000..7464618
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/SubRequestSessionSubscriber.php
@@ -0,0 +1,66 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\SubRequestSessionSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\Authentication\AuthenticationProviderInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Sub-request subscriber.
+ *
+ * Hand session down to sub-requests.
+ */
+class SubRequestSessionSubscriber implements EventSubscriberInterface {
+
+  /**
+   * Current request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * Constructs a new SubRequestSessionSubscriber object.
+   *
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   The request stack.
+   */
+  public function __construct(RequestStack $request_stack) {
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * If the request is a sub-request, ensures session is copied from master.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
+   *   The request event.
+   */
+  public function onRequest(GetResponseEvent $event) {
+    if ($event->getRequestType() == HttpKernelInterface::SUB_REQUEST) {
+      $parent = $this->requestStack->getParentRequest();
+      if ($parent->hasSession()) {
+        $request = $event->getRequest();
+        $request->setSession($parent->getSession());
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[KernelEvents::REQUEST][] = ['onRequest', 50];
+    return $events;
+  }
+}
diff --git a/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php b/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
index 6e93561..7899bdc 100644
--- a/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
+++ b/core/lib/Drupal/Core/Installer/InstallerServiceProvider.php
@@ -53,9 +53,6 @@ public function register(ContainerBuilder $container) {
     // @todo Convert installer steps into routes; add an installer.routing.yml.
     $definition = $container->getDefinition('router.builder');
     $definition->setClass('Drupal\Core\Installer\InstallerRouteBuilder');
-
-    // Remove dependencies on Drupal's default session handling.
-    $container->removeDefinition('authentication.cookie');
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Session/SessionManager.php b/core/lib/Drupal/Core/Session/SessionManager.php
index 157f303..6cc8feb 100644
--- a/core/lib/Drupal/Core/Session/SessionManager.php
+++ b/core/lib/Drupal/Core/Session/SessionManager.php
@@ -329,7 +329,7 @@ protected function getSessionDataMask() {
     // Ignore attribute bags when they do not contain any data.
     foreach ($this->bags as $bag) {
       $key = $bag->getStorageKey();
-      $mask[$key] = empty($_SESSION[$key]);
+      $mask[$key] = !empty($_SESSION[$key]);
     }
 
     return array_intersect_key($mask, $_SESSION);
diff --git a/core/lib/Drupal/Core/StackMiddleware/Session.php b/core/lib/Drupal/Core/StackMiddleware/Session.php
new file mode 100644
index 0000000..1435f1e
--- /dev/null
+++ b/core/lib/Drupal/Core/StackMiddleware/Session.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\StackMiddleware\Session.
+ */
+
+namespace Drupal\Core\StackMiddleware;
+
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Session\SessionInterface;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
+
+/**
+ * Executes the page caching before the main kernel takes over the request.
+ */
+class Session implements HttpKernelInterface {
+
+  /**
+   * The wrapped HTTP kernel.
+   *
+   * @var \Symfony\Component\HttpKernel\HttpKernelInterface
+   */
+  protected $httpKernel;
+
+  /**
+   * The session.
+   *
+   * @var \Symfony\Component\HttpFoundation\Session\SessionInterface
+   */
+  protected $session;
+
+  /**
+   * Constructs a Session stack middleware object.
+   *
+   * @param \Symfony\Component\HttpKernel\HttpKernelInterface $http_kernel
+   *   The decorated kernel.
+   * @param \Symfony\Component\HttpFoundation\Session\SessionInterface $session
+   *   The session.
+   */
+  public function __construct(HttpKernelInterface $http_kernel, SessionInterface $session) {
+    $this->httpKernel = $http_kernel;
+    $this->session = $session;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function handle(Request $request, $type = self::MASTER_REQUEST, $catch = TRUE) {
+    if ($type === self::MASTER_REQUEST) {
+      $request->setSession($this->session);
+    }
+
+    $result = $this->httpKernel->handle($request, $type, $catch);
+
+    if ($type === self::MASTER_REQUEST && $request->hasSession()) {
+      $request->getSession()->save();
+    }
+
+    return $result;
+  }
+
+}
diff --git a/core/modules/system/tests/modules/session_test/session_test.services.yml b/core/modules/system/tests/modules/session_test/session_test.services.yml
index 281b09d..8ef2e20 100644
--- a/core/modules/system/tests/modules/session_test/session_test.services.yml
+++ b/core/modules/system/tests/modules/session_test/session_test.services.yml
@@ -1,6 +1,5 @@
 services:
   session_test.subscriber:
     class: Drupal\session_test\EventSubscriber\SessionTestSubscriber
-    arguments: ['@session_manager']
     tags:
       - { name: event_subscriber }
diff --git a/core/modules/system/tests/modules/session_test/src/EventSubscriber/SessionTestSubscriber.php b/core/modules/system/tests/modules/session_test/src/EventSubscriber/SessionTestSubscriber.php
index 9029093..a17cd5a 100644
--- a/core/modules/system/tests/modules/session_test/src/EventSubscriber/SessionTestSubscriber.php
+++ b/core/modules/system/tests/modules/session_test/src/EventSubscriber/SessionTestSubscriber.php
@@ -7,7 +7,6 @@
 
 namespace Drupal\session_test\EventSubscriber;
 
-use Drupal\Core\Session\SessionManagerInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\KernelEvents;
@@ -20,13 +19,6 @@
 class SessionTestSubscriber implements EventSubscriberInterface {
 
   /**
-   * The session manager.
-   *
-   * @var \Drupal\Core\Session\SessionManagerInterface
-   */
-  protected $sessionManager;
-
-  /**
    * Stores whether $_SESSION is empty at the beginning of the request.
    *
    * @var bool
@@ -34,20 +26,14 @@ class SessionTestSubscriber implements EventSubscriberInterface {
   protected $emptySession;
 
   /**
-   * Constructs a new session test subscriber.
-   */
-  public function __construct(SessionManagerInterface $session_manager) {
-    $this->sessionManager = $session_manager;
-  }
-
-  /**
    * Set header for session testing.
    *
    * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
    *   The Event to process.
    */
   public function onKernelRequestSessionTest(GetResponseEvent $event) {
-    $this->emptySession = (int) !$this->sessionManager->start();
+    $session = $event->getRequest()->getSession();
+    $this->emptySession = (int) !($session && $session->start());
   }
 
   /**
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 2ac5fec..1413e49 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -123,7 +123,7 @@ public function postSave(EntityStorageInterface $storage, $update = TRUE) {
       if ($this->pass->value != $this->original->pass->value) {
         $session_manager->delete($this->id());
         if ($this->id() == \Drupal::currentUser()->id()) {
-          $session_manager->regenerate();
+          \Drupal::service('session')->migrate();
         }
       }
 
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index a251fd7..845ede1 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -614,7 +614,7 @@ function user_login_finalize(UserInterface $account) {
   // This is called before hook_user_login() in case one of those functions
   // fails or incorrectly does a redirect which would leave the old session
   // in place.
-  \Drupal::service('session_manager')->regenerate();
+  \Drupal::service('session')->migrate();
 
   \Drupal::moduleHandler()->invokeAll('user_login', array($account));
 }
@@ -844,7 +844,7 @@ function _user_cancel($edit, $account, $method) {
 function _user_cancel_session_regenerate() {
   // Regenerate the users session instead of calling session_destroy() as we
   // want to preserve any messages that might have been set.
-  \Drupal::service('session_manager')->regenerate();
+  \Drupal::service('session')->migrate();
 }
 
 /**
@@ -1484,5 +1484,9 @@ function user_logout() {
   \Drupal::moduleHandler()->invokeAll('user_logout', array($user));
 
   // Destroy the current session, and reset $user to the anonymous user.
+  // Note: In Symfony the session is intended to be destroyed with
+  // Session::invalidate(). Regrettably this method is currently broken and may
+  // lead to the creation of spurious session records in the database.
+  // @see https://github.com/symfony/symfony/issues/12375
   session_destroy();
 }
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index e89aa73..7c35d88 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -17,7 +17,6 @@ services:
       - { name: access_check, applies_to: _user_is_logged_in }
   authentication.cookie:
     class: Drupal\Core\Authentication\Provider\Cookie
-    arguments: ['@session_manager']
     tags:
       - { name: authentication_provider, priority: 0 }
   cache_context.user:
