diff --git a/core/modules/toolbar/lib/Drupal/toolbar/Access/SubtreeAccess.php b/core/modules/toolbar/lib/Drupal/toolbar/Access/SubtreeAccess.php
new file mode 100644
index 0000000000000000000000000000000000000000..6f8a90e5fa5b901d8abad829c49fbfd6bd62d83b
--- /dev/null
+++ b/core/modules/toolbar/lib/Drupal/toolbar/Access/SubtreeAccess.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\toolbar\Access\SubtreeAccess.
+ */
+
+namespace Drupal\toolbar\Access;
+
+use Drupal\Core\Access\AccessCheckInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Defines a special access checker for the toolbar subtree route.
+ */
+class SubtreeAccess implements AccessCheckInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function applies(Route $route) {
+    return array_key_exists('_access_toolbar_subtree', $route->getRequirements());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $hash = $request->get('hash');
+    if (user_access('access toolbar') && ($hash == _toolbar_get_subtree_hash())) {
+      return TRUE;
+    }
+    else {
+      return NULL;
+    }
+  }
+
+}
diff --git a/core/modules/toolbar/lib/Drupal/toolbar/Routing/ToolbarController.php b/core/modules/toolbar/lib/Drupal/toolbar/Routing/ToolbarController.php
new file mode 100644
index 0000000000000000000000000000000000000000..af1935fcecf72375b550fded434985de02e1703f
--- /dev/null
+++ b/core/modules/toolbar/lib/Drupal/toolbar/Routing/ToolbarController.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\toolbar\Routing\ToolbarController.
+ */
+
+namespace Drupal\toolbar\Routing;
+
+use Symfony\Component\HttpFoundation\JsonResponse;
+
+/**
+ * Defines a controller for the toolbar module.
+ */
+class ToolbarController {
+
+  /**
+   * Returns the rendered subtree of each top-level toolbar link.
+   *
+   * @return \Symfony\Component\HttpFoundation\JsonResponse
+   */
+  public function subtreesJsonp() {
+    _toolbar_initialize_page_cache();
+    $subtrees = toolbar_get_rendered_subtrees();
+    $response = new JsonResponse($subtrees);
+    $response->setCallback('Drupal.toolbar.setSubtrees');
+    return $response;
+  }
+
+}
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index e9ef4efb4770896d13cfcd3fe5b14ea3df57ce13..5bc453498acbd149818ec6a7f26ad9c8088325af 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -59,20 +59,6 @@ function toolbar_theme($existing, $type, $theme, $path) {
   return $items;
 }
 
-/**
- * Implements hook_menu().
- */
-function toolbar_menu() {
-  $items['toolbar/subtrees/%'] = array(
-    'page callback' => 'toolbar_subtrees_jsonp',
-    'page arguments' => array(2),
-    'access callback' => '_toolbar_subtrees_access',
-    'access arguments' => array(2),
-    'type' => MENU_CALLBACK,
-  );
-  return $items;
-}
-
 /**
  * Implements hook_element_info().
  */
@@ -125,28 +111,6 @@ function toolbar_element_info() {
   return $elements;
 }
 
-/**
- * Access callback: Returns if the user has access to the rendered subtree requested by the hash.
- *
- * @see toolbar_menu().
- */
-function _toolbar_subtrees_access($hash) {
-  return user_access('access toolbar') && ($hash == _toolbar_get_subtree_hash());
-}
-
-/**
- * Page callback: Returns the rendered subtree of each top-level toolbar link.
- *
- * @see toolbar_menu().
- */
-function toolbar_subtrees_jsonp($hash) {
-  _toolbar_initialize_page_cache();
-  $subtrees = toolbar_get_rendered_subtrees();
-  $response = new JsonResponse($subtrees);
-  $response->setCallback('Drupal.toolbar.setSubtrees');
-  return $response;
-}
-
 /**
  * Use Drupal's page cache for toolbar/subtrees/*, even for authenticated users.
  *
diff --git a/core/modules/toolbar/toolbar.routing.yml b/core/modules/toolbar/toolbar.routing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..448ea99a4d58f0e9a0601b43bba2949c82da2cae
--- /dev/null
+++ b/core/modules/toolbar/toolbar.routing.yml
@@ -0,0 +1,6 @@
+toolbar_subtrees:
+  pattern: '/toolbar/subtrees/{hash}'
+  defaults:
+    _controller: '\Drupal\toolbar\Routing\ToolbarController::subtreesJsonp'
+  requirements:
+    _access_toolbar_subtree: 'TRUE'
diff --git a/core/modules/toolbar/toolbar.services.yml b/core/modules/toolbar/toolbar.services.yml
index 7f269683c143d03c18f0440af7bced9a90be11a9..c700b62941ad0fb2b9ce2939208b5a28125c35c0 100644
--- a/core/modules/toolbar/toolbar.services.yml
+++ b/core/modules/toolbar/toolbar.services.yml
@@ -1,4 +1,8 @@
 services:
+  access_check.toolbar_subtree:
+    class: Drupal\toolbar\Access\SubtreeAccess
+    tags:
+      - { name: access_check }
   cache.toolbar:
     class: Drupal\Core\Cache\CacheBackendInterface
     tags: