diff --git a/core/core.services.yml b/core/core.services.yml
index 022fa7e..5376fd4 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -604,9 +604,13 @@ services:
     tags:
       - { name: access_check, applies_to: _csrf_token }
     arguments: ['@csrf_token']
+  maintenance_mode_exemption:
+    class: Drupal\Core\Site\ChainMaintenanceExemption
+    tags:
+      - { name: service_collector, tag: maintenance_mode_exemption, call: addExemption }
   maintenance_mode_subscriber:
     class: Drupal\Core\EventSubscriber\MaintenanceModeSubscriber
-    arguments: ['@state', '@config.factory', '@string_translation', '@url_generator', '@current_user']
+    arguments: ['@state', '@maintenance_mode_exemption', '@string_translation', '@url_generator', '@current_user']
     tags:
       - { name: event_subscriber }
   path_subscriber:
diff --git a/core/lib/Drupal/Core/Controller/ExceptionController.php b/core/lib/Drupal/Core/Controller/ExceptionController.php
index 320cb06..7183019 100644
--- a/core/lib/Drupal/Core/Controller/ExceptionController.php
+++ b/core/lib/Drupal/Core/Controller/ExceptionController.php
@@ -18,6 +18,7 @@
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Drupal\Component\Utility\String;
+use Drupal\Component\Utility\Xss;
 use Symfony\Component\Debug\Exception\FlattenException;
 use Drupal\Core\ContentNegotiation;
 use Drupal\Core\Utility\Error;
@@ -324,6 +325,29 @@ public function on500Html(FlattenException $exception, Request $request) {
   }
 
   /**
+   * Deliver the 503 page if the site is in maintenance mode.
+   *
+   * @param \Symfony\Component\Debug\Exception\FlattenException $exception
+   *   The flattened exception.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request object that triggered this exception.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   *   A response object.
+   */
+  public function on503Html(FlattenException $exception, Request $request) {
+    $maintenance_message = $this->container->get('config.factory')->get('system.maintenance')->get('message');
+    $site_name = $this->container->get('config.factory')->get('system.site')->get('name');
+
+    $content = Xss::filterAdmin(String::format($maintenance_message, array('@site' => $site_name)));
+    $output = DefaultHtmlPageRenderer::renderPage($content, $this->t('Site under maintenance'));
+    $response = new Response($output);
+    $response->setStatusCode(503, 'Service Unavailable');
+
+    return $response;
+  }
+
+  /**
    * Processes an AccessDenied exception that occurred on a JSON request.
    *
    * @param \Symfony\Component\Debug\Exception\FlattenException $exception
diff --git a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
index 1b665f3..c5cd0d0 100644
--- a/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/MaintenanceModeSubscriber.php
@@ -7,20 +7,18 @@
 
 namespace Drupal\Core\EventSubscriber;
 
-use Drupal\Component\Utility\String;
-use Drupal\Component\Utility\Xss;
-use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\Page\DefaultHtmlPageRenderer;
+use Drupal\Core\Routing\RouteMatch;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Site\MaintenanceExemptionInterface;
 use Drupal\Core\State\StateInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
-use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
+use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
 
 /**
@@ -31,25 +29,25 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
   use StringTranslationTrait;
 
   /**
-   * The current account.
+   * The state.
    *
-   * @var \Drupal\Core\Session\AccountInterface
+   * @var \Drupal\Core\State\StateInterface
    */
-  protected $account;
+  protected $state;
 
   /**
-   * The state.
+   * The maintenance mode exemption.
    *
-   * @var \Drupal\Core\State\StateInterface
+   * @var \Drupal\Core\Site\MaintenanceExemptionInterface
    */
-  protected $state;
+  protected $maintenanceExemption;
 
   /**
-   * The config factory.
+   * The current account.
    *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
+   * @var \Drupal\Core\Session\AccountInterface
    */
-  protected $config;
+  protected $account;
 
   /**
    * The url generator.
@@ -59,32 +57,12 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
   protected $urlGenerator;
 
   /**
-   * @defgroup menu_status_codes Menu status codes
-   * @{
-   * Status codes to be used to check the maintenance code.
-   */
-
-  /**
-   * Internal menu status code -- Menu item inaccessible because site is offline.
-   */
-  const SITE_OFFLINE = 4;
-
-  /**
-   * Internal menu status code -- Everything is working fine.
-   */
-  const SITE_ONLINE = 5;
-
-  /**
-   * @} End of "defgroup menu_status_codes".
-   */
-
-  /**
    * Constructs a new MaintenanceModeSubscriber.
    *
    * @param \Drupal\Core\State\StateInterface $state
    *   The state.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The config factory.
+   * @param \Drupal\Core\Site\MaintenanceExemptionInterface $maintenance_exemption
+   *   The maintenance mode exemption.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
    *   The string translation.
    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
@@ -92,79 +70,42 @@ class MaintenanceModeSubscriber implements EventSubscriberInterface {
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The current user.
    */
-  public function __construct(StateInterface $state, ConfigFactoryInterface $config_factory, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account) {
+  public function __construct(StateInterface $state, MaintenanceExemptionInterface $maintenance_exemption, TranslationInterface $translation, UrlGeneratorInterface $url_generator, AccountInterface $account) {
     $this->state = $state;
-    $this->config = $config_factory;
+    $this->maintenanceExemption = $maintenance_exemption;
     $this->stringTranslation = $translation;
     $this->urlGenerator = $url_generator;
     $this->account = $account;
   }
 
   /**
-   * Determine whether the page is configured to be offline.
-   *
-   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
-   *   The Event to process.
-   */
-  public function onKernelRequestDetermineSiteStatus(GetResponseEvent $event) {
-    // Check if the site is offline.
-    $request = $event->getRequest();
-    $is_offline = $this->isSiteInMaintenance($request) ? static::SITE_OFFLINE : static::SITE_ONLINE;
-    $request->attributes->set('_maintenance', $is_offline);
-  }
-
-  /**
    * Returns the site maintenance page if the site is offline.
    *
    * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
    *   The event to process.
    */
   public function onKernelRequestMaintenance(GetResponseEvent $event) {
-    $request = $event->getRequest();
-    $response = $event->getResponse();
-    // Continue if the site is online and the response is not a redirection.
-    if ($request->attributes->get('_maintenance') != static::SITE_ONLINE && !($response instanceof RedirectResponse)) {
-      // Deliver the 503 page.
-      drupal_maintenance_theme();
-      $content = Xss::filterAdmin(String::format($this->config->get('system.maintenance')->get('message'), array(
-        '@site' => $this->config->get('system.site')->get('name'),
-      )));
-      $content = DefaultHtmlPageRenderer::renderPage($content, t('Site under maintenance'));
-      $response = new Response('Service unavailable', 503);
-      $response->setContent($content);
-      $event->setResponse($response);
-    }
-
-    $can_access_maintenance = $this->account->hasPermission('access site in maintenance mode');
-    $is_maintenance = $this->state->get('system.maintenance_mode');
-    // Ensure that the maintenance mode message is displayed only once
-    // (allowing for page redirects) and specifically suppress its display on
-    // the maintenance mode settings page.
-    $is_maintenance_route = $request->attributes->get(RouteObjectInterface::ROUTE_NAME) == 'system.site_maintenance_mode';
-    if ($is_maintenance && $can_access_maintenance && !$is_maintenance_route) {
-      if ($this->account->hasPermission('administer site configuration')) {
-        $this->drupalSetMessage($this->t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => $this->urlGenerator->generate('system.site_maintenance_mode'))), 'status', FALSE);
+    if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST && $this->state->get('system.maintenance_mode')) {
+      $route_match = RouteMatch::createFromRequest($event->getRequest());
+      if (!$this->maintenanceExemption->exempt($route_match, $this->account)) {
+        // Deliver the 503 page if the site is in maintenance mode and the
+        // logged in user is not allowed to bypass it.
+        throw new ServiceUnavailableHttpException();
       }
       else {
-        $this->drupalSetMessage($this->t('Operating in maintenance mode.'), 'status', FALSE);
-      }
-    }
-  }
-
-  /**
-   * Checks whether the site is in maintenance mode.
-   *
-   * @return bool
-   *   FALSE if the site is not in maintenance mode
-   */
-  protected function isSiteInMaintenance() {
-    // Check if site is in maintenance mode.
-    if ($this->state->get('system.maintenance_mode')) {
-      if (!$this->account->hasPermission('access site in maintenance mode')) {
-        return TRUE;
+        // Display a message if the logged in user has access to the site in
+        // maintenance mode. However, suppress it on the maintenance mode
+        // setting spage.
+        if ($route_match->getRouteName() != 'system.site_maintenance_mode') {
+          if ($this->account->hasPermission('administer site configuration')) {
+            $this->drupalSetMessage($this->t('Operating in maintenance mode. <a href="@url">Go online.</a>', array('@url' => $this->urlGenerator->generate('system.site_maintenance_mode'))), 'status', FALSE);
+          }
+          else {
+            $this->drupalSetMessage($this->t('Operating in maintenance mode.'), 'status', FALSE);
+          }
+        }
       }
     }
-    return FALSE;
   }
 
   /**
@@ -177,10 +118,7 @@ protected function drupalSetMessage($message = NULL, $type = 'status', $repeat =
   /**
    * {@inheritdoc}
    */
-  static function getSubscribedEvents() {
-    // In order to change the maintenance status an event subscriber with a
-    // priority between 30 and 40 should be added.
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestDetermineSiteStatus', 40);
+  public static function getSubscribedEvents() {
     $events[KernelEvents::REQUEST][] = array('onKernelRequestMaintenance', 30);
     return $events;
   }
diff --git a/core/lib/Drupal/Core/Site/ChainMaintenanceExemption.php b/core/lib/Drupal/Core/Site/ChainMaintenanceExemption.php
new file mode 100644
index 0000000..8b526d9
--- /dev/null
+++ b/core/lib/Drupal/Core/Site/ChainMaintenanceExemption.php
@@ -0,0 +1,46 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Site\ChainMaintenanceExemption
+ */
+
+namespace Drupal\Core\Site;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Provides the default implementation of the maintenance exemption service.
+ */
+class ChainMaintenanceExemption implements ChainMaintenanceExemptionInterface {
+
+  /**
+   * A list of maintenance mode exemption handlers.
+   *
+   * @var \Drupal\Core\Site\MaintenanceExemptionInterface[]
+   */
+  protected $exemptionHandlers = [];
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exempt(RouteMatchInterface $route_match, AccountInterface $account) {
+    foreach ($this->exemptionHandlers as $handler) {
+      if ($handler->exempt($route_match, $account)) {
+        return TRUE;
+      }
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function addExemption(MaintenanceExemptionInterface $handler) {
+    $this->exemptionHandlers[] = $handler;
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Site/ChainMaintenanceExemptionInterface.php b/core/lib/Drupal/Core/Site/ChainMaintenanceExemptionInterface.php
new file mode 100644
index 0000000..e3c508e
--- /dev/null
+++ b/core/lib/Drupal/Core/Site/ChainMaintenanceExemptionInterface.php
@@ -0,0 +1,25 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Site\ChainMaintenanceExemptionInterface.
+ */
+
+namespace Drupal\Core\Site;
+
+/**
+ * Provides an interface for chained maintenance mode exemption.
+ */
+interface ChainMaintenanceExemptionInterface extends MaintenanceExemptionInterface {
+
+  /**
+   * Add an exemption handler.
+   *
+   * @param \Drupal\Core\Site\MaintenanceExemptionInterface $handler
+   *   A maintenance exemption handler.
+   *
+   * @returns $this
+   */
+  public function addExemption(MaintenanceExemptionInterface $handler);
+
+}
diff --git a/core/lib/Drupal/Core/Site/MaintenanceExemptionInterface.php b/core/lib/Drupal/Core/Site/MaintenanceExemptionInterface.php
new file mode 100644
index 0000000..01a487f
--- /dev/null
+++ b/core/lib/Drupal/Core/Site/MaintenanceExemptionInterface.php
@@ -0,0 +1,30 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\Core\Site\MaintenanceExemptionInterface.
+ */
+
+namespace Drupal\Core\Site;
+
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+
+/**
+ * Provides an interface for maintenance mode exemption rule implementations.
+ */
+interface MaintenanceExemptionInterface {
+
+  /**
+   * Returns TRUE if the given account should be exempted from maintenance mode.
+   *
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The current route match.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The logged in user.
+   *
+   * @return bool
+   *   TRUE if the user should be exempted from maintenance mode.
+   */
+  public function exempt(RouteMatchInterface $route_match, AccountInterface $account);
+
+}
diff --git a/core/modules/system/src/Site/MaintenanceExemption.php b/core/modules/system/src/Site/MaintenanceExemption.php
new file mode 100644
index 0000000..22036f5
--- /dev/null
+++ b/core/modules/system/src/Site/MaintenanceExemption.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Site\MaintenanceExemption.
+ */
+
+namespace Drupal\system\Site;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Site\MaintenanceExemptionInterface;
+
+/**
+ * Exempts administrators from the maintenance mode.
+ */
+class MaintenanceExemption implements MaintenanceExemptionInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exempt(RouteMatchInterface $route_match, AccountInterface $account) {
+    if ($account->hasPermission('access site in maintenance mode')) {
+      return TRUE;
+    }
+  }
+
+}
diff --git a/core/modules/system/system.services.yml b/core/modules/system/system.services.yml
index 2b7046c..9913425 100644
--- a/core/modules/system/system.services.yml
+++ b/core/modules/system/system.services.yml
@@ -33,3 +33,7 @@ services:
     arguments: ['@cron', '@config.factory', '@state']
     tags:
       - { name: event_subscriber }
+  system.maintenance_mode_exemption:
+    class: Drupal\system\Site\MaintenanceExemption
+    tags:
+      - { name: maintenance_mode_exemption }
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.services.yml b/core/modules/system/tests/modules/menu_test/menu_test.services.yml
index b52f8cf..951274f 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.services.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.services.yml
@@ -1,8 +1,8 @@
 services:
-  menu_test_maintenance_mode_subscriber:
-    class: Drupal\menu_test\EventSubscriber\MaintenanceModeSubscriber
+  menu_test_maintenance_mode_exemption:
+    class: Drupal\menu_test\Site\MaintenanceExemption
     tags:
-      - { name: event_subscriber }
+      - { name: maintenance_mode_exemption }
 
   theme.negotiator.test_theme:
     class: Drupal\menu_test\Theme\TestThemeNegotiator
diff --git a/core/modules/system/tests/modules/menu_test/src/EventSubscriber/MaintenanceModeSubscriber.php b/core/modules/system/tests/modules/menu_test/src/EventSubscriber/MaintenanceModeSubscriber.php
deleted file mode 100644
index 0b6ae61..0000000
--- a/core/modules/system/tests/modules/menu_test/src/EventSubscriber/MaintenanceModeSubscriber.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\menu_test\EventSubscriber\MaintenanceModeSubscriber.
- */
-
-namespace Drupal\menu_test\EventSubscriber;
-
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber as CoreMaintenanceModeSubscriber;
-
-
-/**
- * Maintenance mode subscriber to set site online on a test.
- */
-class MaintenanceModeSubscriber implements EventSubscriberInterface {
-
-  /**
-   * Set the page online if called from a certain path.
-   *
-   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
-   *   The event to process.
-   */
-  public function onKernelRequestMaintenance(GetResponseEvent $event) {
-    $request = $event->getRequest();
-    // Allow access to menu_login_callback even if in maintenance mode.
-    if ($request->attributes->get('_maintenance') == CoreMaintenanceModeSubscriber::SITE_OFFLINE && $request->attributes->get('_system_path') == 'menu_login_callback') {
-      $request->attributes->set('_maintenance', CoreMaintenanceModeSubscriber::SITE_ONLINE);
-    }
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public static function getSubscribedEvents() {
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestMaintenance', 35);
-    return $events;
-  }
-
-}
diff --git a/core/modules/system/tests/modules/menu_test/src/Site/MaintenanceExemption.php b/core/modules/system/tests/modules/menu_test/src/Site/MaintenanceExemption.php
new file mode 100644
index 0000000..750a83a
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/src/Site/MaintenanceExemption.php
@@ -0,0 +1,29 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_test\Site\MaintenanceExemption.
+ */
+
+namespace Drupal\menu_test\Site;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Site\MaintenanceExemptionInterface;
+
+
+/**
+ * Maintenance mode subscriber to set site online on a test.
+ */
+class MaintenanceExemption implements MaintenanceExemptionInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exempt(RouteMatchInterface $route_match, AccountInterface $account) {
+    if ($route_match->getRouteName() == 'menu_test.login_callback') {
+      return TRUE;
+    }
+  }
+
+}
diff --git a/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php b/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
index 1e8d114..63f250b 100644
--- a/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
+++ b/core/modules/user/src/EventSubscriber/MaintenanceModeSubscriber.php
@@ -7,11 +7,14 @@
 
 namespace Drupal\user\EventSubscriber;
 
+use Drupal\Core\Routing\RouteMatch;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Site\MaintenanceExemptionInterface;
+use Drupal\Core\State\StateInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
 use Symfony\Component\HttpKernel\KernelEvents;
-use Drupal\Core\EventSubscriber\MaintenanceModeSubscriber as CoreMaintenanceModeSubscriber;
 
 /**
  * Maintenance mode subscriber to logout users.
@@ -19,46 +22,68 @@
 class MaintenanceModeSubscriber implements EventSubscriberInterface {
 
   /**
+   * The state.
+   *
+   * @var \Drupal\Core\State\StateInterface
+   */
+  protected $state;
+
+  /**
+   * The maintenance mode exemption.
+   *
+   * @var \Drupal\Core\Site\MaintenanceExemptionInterface
+   */
+  protected $maintenanceExemption;
+
+  /**
+   * The current account.
+   *
+   * @var \Drupal\Core\Session\AccountInterface
+   */
+  protected $account;
+
+  /**
+   * Constructs a new MaintenanceModeSubscriber.
+   *
+   * @param \Drupal\Core\State\StateInterface $state
+   *   The state.
+   * @param \Drupal\Core\Site\MaintenanceExemptionInterface $maintenance_exemption
+   *   The maintenance mode exemption.
+   * @param \Drupal\Core\Session\AccountInterface $account
+   *   The current user.
+   */
+  public function __construct(StateInterface $state, MaintenanceExemptionInterface $maintenance_exemption, AccountInterface $account) {
+    $this->state = $state;
+    $this->maintenanceExemption = $maintenance_exemption;
+    $this->account = $account;
+  }
+
+  /**
    * Determine whether the page is configured to be offline.
    *
    * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
    *   The event to process.
    */
   public function onKernelRequestMaintenance(GetResponseEvent $event) {
-    $user = \Drupal::currentUser();
     $request = $event->getRequest();
-    $site_status = $request->attributes->get('_maintenance');
     $path = $request->attributes->get('_system_path');
-    if ($site_status == CoreMaintenanceModeSubscriber::SITE_OFFLINE) {
+    if ($this->state->get('system.maintenance_mode')) {
+      $route_match = RouteMatch::createFromRequest($request);
       // If the site is offline, log out unprivileged users.
-      if ($user->isAuthenticated() && !$user->hasPermission('access site in maintenance mode')) {
+      if ($this->account->isAuthenticated() && !$this->maintenanceExemption->exempt($route_match, $this->account)) {
         user_logout();
         // Redirect to homepage.
         $event->setResponse(new RedirectResponse(url('<front>', array('absolute' => TRUE))));
         return;
       }
 
-      if ($user->isAnonymous()) {
-        switch ($path) {
-          case 'user':
-            // Forward anonymous user to login page.
-            $event->setResponse(new RedirectResponse(url('user/login', array('absolute' => TRUE))));
-            return;
-          case 'user/login':
-          case 'user/password':
-            // Disable offline mode.
-            $request->attributes->set('_maintenance', CoreMaintenanceModeSubscriber::SITE_ONLINE);
-            break;
-          default:
-            if (strpos($path, 'user/reset/') === 0) {
-              // Disable offline mode.
-              $request->attributes->set('_maintenance', CoreMaintenanceModeSubscriber::SITE_ONLINE);
-            }
-            break;
-        }
+      if ($this->account->isAnonymous() && $path == 'user') {
+        // Forward anonymous user to login page.
+        $event->setResponse(new RedirectResponse(url('user/login', array('absolute' => TRUE))));
+        return;
       }
     }
-    if ($user->isAuthenticated()) {
+    if ($this->account->isAuthenticated()) {
       if ($path == 'user/login') {
         // If user is logged in, redirect to 'user' instead of giving 403.
         $event->setResponse(new RedirectResponse(url('user', array('absolute' => TRUE))));
@@ -66,7 +91,7 @@ public function onKernelRequestMaintenance(GetResponseEvent $event) {
       }
       if ($path == 'user/register') {
         // Authenticated user should be redirected to user edit page.
-        $event->setResponse(new RedirectResponse(url('user/' . $user->id() . '/edit', array('absolute' => TRUE))));
+        $event->setResponse(new RedirectResponse(url('user/' . $this->account->id() . '/edit', array('absolute' => TRUE))));
         return;
       }
     }
diff --git a/core/modules/user/src/Site/MaintenanceExemption.php b/core/modules/user/src/Site/MaintenanceExemption.php
new file mode 100644
index 0000000..6e3ab4b
--- /dev/null
+++ b/core/modules/user/src/Site/MaintenanceExemption.php
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Site\MaintenanceExemption.
+ */
+
+namespace Drupal\user\Site;
+
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Site\MaintenanceExemptionInterface;
+
+/**
+ * Exempts login and password-retrieval form from the maintenance mode.
+ */
+class MaintenanceExemption implements MaintenanceExemptionInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function exempt(RouteMatchInterface $route_match, AccountInterface $account) {
+    $route_name = $route_match->getRouteName();
+    return in_array($route_name, ['user.login', 'user.pass', 'user.reset']);
+  }
+
+}
diff --git a/core/modules/user/user.services.yml b/core/modules/user/user.services.yml
index 1e8f9b3..97339e9 100644
--- a/core/modules/user/user.services.yml
+++ b/core/modules/user/user.services.yml
@@ -38,6 +38,7 @@ services:
     arguments: ['@database', '@config.factory', '@entity.manager', '@entity.query']
   user_maintenance_mode_subscriber:
     class: Drupal\user\EventSubscriber\MaintenanceModeSubscriber
+    arguments: ['@state', '@maintenance_mode_exemption', '@current_user']
     tags:
       - { name: event_subscriber }
   theme.negotiator.admin_theme:
@@ -54,3 +55,7 @@ services:
   user.tempstore:
     class: Drupal\user\TempStoreFactory
     arguments: ['@serialization.phpserialize', '@database', '@lock']
+  user.maintenance_mode_exemption:
+    class: Drupal\user\Site\MaintenanceExemption
+    tags:
+      - { name: maintenance_mode_exemption }
