diff --git a/core/core.services.yml b/core/core.services.yml
index 6310c21..888307f 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -817,7 +817,7 @@ services:
       - { name: event_subscriber }
   url_generator.non_bubbling:
     class: Drupal\Core\Routing\UrlGenerator
-    arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '%filter_protocols%']
+    arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@request_stack', '@router.route_preloader', '%filter_protocols%']
     public: false
     calls:
       - [setContext, ['@?router.request_context']]
diff --git a/core/lib/Drupal/Core/Routing/RoutePreloader.php b/core/lib/Drupal/Core/Routing/RoutePreloader.php
index 2d382bb..93553c7 100644
--- a/core/lib/Drupal/Core/Routing/RoutePreloader.php
+++ b/core/lib/Drupal/Core/Routing/RoutePreloader.php
@@ -7,8 +7,7 @@
 use Drupal\Core\State\StateInterface;
 use Symfony\Component\EventDispatcher\Event;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpKernel\Event\KernelEvent;
-use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Defines a class which preloads non-admin routes.
@@ -65,12 +64,12 @@ public function __construct(RouteProviderInterface $route_provider, StateInterfa
   /**
    * Loads all non-admin routes right before the actual page is rendered.
    *
-   * @param \Symfony\Component\HttpKernel\Event\KernelEvent $event
-   *   The event to process.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The request.
    */
-  public function onRequest(KernelEvent $event) {
+  public function preloadRoutes(Request $request) {
     // Only preload on normal HTML pages, as they will display menu links.
-    if ($this->routeProvider instanceof PreloadableRouteProviderInterface && $event->getRequest()->getRequestFormat() == 'html') {
+    if ($this->routeProvider instanceof PreloadableRouteProviderInterface && $request->getRequestFormat() == 'html') {
 
       // Ensure that the state query is cached to skip the database query, if
       // possible.
@@ -124,9 +123,6 @@ public static function getSubscribedEvents() {
     // Set a really low priority to catch as many as possible routes.
     $events[RoutingEvents::ALTER] = array('onAlterRoutes', -1024);
     $events[RoutingEvents::FINISHED] = array('onFinishedRoutes');
-    // Load the routes before the controller is executed (which happens after
-    // the kernel request event).
-    $events[KernelEvents::REQUEST][] = array('onRequest');
     return $events;
   }
 
diff --git a/core/lib/Drupal/Core/Routing/UrlGenerator.php b/core/lib/Drupal/Core/Routing/UrlGenerator.php
index ccf2b60..a16452d 100644
--- a/core/lib/Drupal/Core/Routing/UrlGenerator.php
+++ b/core/lib/Drupal/Core/Routing/UrlGenerator.php
@@ -63,6 +63,20 @@ class UrlGenerator implements UrlGeneratorInterface {
   ];
 
   /**
+   * The route preloader.
+   *
+   * @var \Drupal\Core\Routing\RoutePreloader
+   */
+  protected $routePreloader;
+
+  /**
+   * Whether the routes have been preloaded already.
+   *
+   * @var bool
+   */
+  protected $preloaded = FALSE;
+
+  /**
    * Constructs a new generator object.
    *
    * @param \Drupal\Core\Routing\RouteProviderInterface $provider
@@ -73,10 +87,12 @@ class UrlGenerator implements UrlGeneratorInterface {
    *   The route processor.
    * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
    *   A request stack object.
+   * @param \Drupal\Core\Routing\RoutePreloader $route_preloader
+   *   The route preloader.
    * @param string[] $filter_protocols
    *   (optional) An array of protocols allowed for URL generation.
    */
-  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, OutboundRouteProcessorInterface $route_processor, RequestStack $request_stack, array $filter_protocols = ['http', 'https']) {
+  public function __construct(RouteProviderInterface $provider, OutboundPathProcessorInterface $path_processor, OutboundRouteProcessorInterface $route_processor, RequestStack $request_stack, RoutePreloader $route_preloader, array $filter_protocols = ['http', 'https']) {
     $this->provider = $provider;
     $this->context = new RequestContext();
 
@@ -84,6 +100,7 @@ public function __construct(RouteProviderInterface $provider, OutboundPathProces
     $this->routeProcessor = $route_processor;
     UrlHelper::setAllowedProtocols($filter_protocols);
     $this->requestStack = $request_stack;
+    $this->routePreloader = $route_preloader;
   }
 
   /**
@@ -91,6 +108,7 @@ public function __construct(RouteProviderInterface $provider, OutboundPathProces
    */
   public function setContext(SymfonyRequestContext $context) {
     $this->context = $context;
+    $this->preloaded = FALSE;
   }
 
   /**
@@ -115,6 +133,16 @@ public function isStrictRequirements() {
   }
 
   /**
+   * Preload the routes.
+   */
+  protected function preloadIfNeeded() {
+    if (!$this->preloaded) {
+      $this->routePreloader->preloadRoutes($this->requestStack->getCurrentRequest());
+      $this->preloaded = TRUE;
+    }
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function getPathFromRoute($name, $parameters = array()) {
@@ -419,6 +447,7 @@ protected function processRoute($name, SymfonyRoute $route, array &$parameters,
    * @see \Drupal\Core\Routing\RouteProviderInterface
    */
   protected function getRoute($name) {
+    $this->preloadIfNeeded();
     if ($name instanceof SymfonyRoute) {
       $route = $name;
     }
diff --git a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php
index 4db4297..651a3ef 100644
--- a/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php
+++ b/core/tests/Drupal/Tests/Core/Routing/RoutePreloaderTest.php
@@ -123,37 +123,29 @@ public function testOnAlterRoutesWithNonAdminRoutes() {
 
   /**
    * Tests onRequest on a non html request.
+   *
+   * @covers ::preloadRoutes
    */
-  public function testOnRequestNonHtml() {
-    $event = $this->getMockBuilder('\Symfony\Component\HttpKernel\Event\KernelEvent')
-      ->disableOriginalConstructor()
-      ->getMock();
+  public function testPreloadRoutesNonHtml() {
     $request = new Request();
     $request->setRequestFormat('non-html');
-    $event->expects($this->any())
-      ->method('getRequest')
-      ->will($this->returnValue($request));
 
     $this->routeProvider->expects($this->never())
       ->method('getRoutesByNames');
     $this->state->expects($this->never())
       ->method('get');
 
-    $this->preloader->onRequest($event);
+    $this->preloader->preloadRoutes($request);
   }
 
   /**
    * Tests onRequest on a html request.
+   *
+   * @covers ::preloadRoutes
    */
-  public function testOnRequestOnHtml() {
-    $event = $this->getMockBuilder('\Symfony\Component\HttpKernel\Event\KernelEvent')
-      ->disableOriginalConstructor()
-      ->getMock();
+  public function testPreloadRoutesOnHtml() {
     $request = new Request();
     $request->setRequestFormat('html');
-    $event->expects($this->any())
-      ->method('getRequest')
-      ->will($this->returnValue($request));
 
     $this->routeProvider->expects($this->once())
       ->method('preLoadRoutes')
@@ -163,7 +155,7 @@ public function testOnRequestOnHtml() {
       ->with('routing.non_admin_routes')
       ->will($this->returnValue(array('test2')));
 
-    $this->preloader->onRequest($event);
+    $this->preloader->preloadRoutes($request);
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
index 06e8c17..88b6c05 100644
--- a/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Routing/UrlGeneratorTest.php
@@ -9,6 +9,7 @@
 use Drupal\Core\PathProcessor\PathProcessorManager;
 use Drupal\Core\Render\BubbleableMetadata;
 use Drupal\Core\Routing\RequestContext;
+use Drupal\Core\Routing\RoutePreloader;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\Routing\UrlGenerator;
 use Drupal\Tests\UnitTestCase;
@@ -62,6 +63,13 @@ class UrlGeneratorTest extends UnitTestCase {
   protected $requestStack;
 
   /**
+   * The mock route preloader.
+   *
+   * @var \Drupal\Core\Routing\RoutePreloader|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $routePreloader;
+
+  /**
    * The request context.
    *
    * @var \Drupal\Core\Routing\RequestContext
@@ -160,8 +168,13 @@ protected function setUp() {
     $this->routeProcessorManager = $this->getMockBuilder('Drupal\Core\RouteProcessor\RouteProcessorManager')
       ->disableOriginalConstructor()
       ->getMock();
+    $this->routePreloader = $this->getMockBuilder(RoutePreloader::class)
+      ->disableOriginalConstructor()
+      ->getMock();
+    $this->routePreloader->expects($this->atMost(1))
+      ->method('preloadRoutes');
 
-    $generator = new UrlGenerator($this->provider, $processor_manager, $this->routeProcessorManager, $this->requestStack, ['http', 'https']);
+    $generator = new UrlGenerator($this->provider, $processor_manager, $this->routeProcessorManager, $this->requestStack, $this->routePreloader, ['http', 'https']);
     $generator->setContext($this->context);
     $this->generator = $generator;
   }
@@ -238,8 +251,9 @@ public function testAliasGenerationUsingInterfaceConstants() {
   public function testUrlGenerationWithDisabledPathProcessing() {
     $path_processor = $this->prophesize(OutboundPathProcessorInterface::class);
     $path_processor->processOutbound(Argument::cetera())->shouldNotBeCalled();
+    $route_preloader = $this->prophesize(RoutePreloader::class);
 
-    $generator = new UrlGenerator($this->provider, $path_processor->reveal(), $this->routeProcessorManager, $this->requestStack, ['http', 'https']);
+    $generator = new UrlGenerator($this->provider, $path_processor->reveal(), $this->routeProcessorManager, $this->requestStack, $route_preloader->reveal(), ['http', 'https']);
     $generator->setContext($this->context);
 
     $url = $this->generator->generateFromRoute('test_1', [], ['path_processing' => FALSE]);
@@ -252,11 +266,12 @@ public function testUrlGenerationWithDisabledPathProcessing() {
   public function testUrlGenerationWithDisabledPathProcessingByRoute() {
     $path_processor = $this->prophesize(OutboundPathProcessorInterface::class);
     $path_processor->processOutbound(Argument::cetera())->shouldNotBeCalled();
+    $route_preloader = $this->prophesize(RoutePreloader::class);
 
     $provider = $this->prophesize(RouteProviderInterface::class);
     $provider->getRouteByName('test_1')->willReturn(new Route('/test/one', [], [], ['default_url_options' => ['path_processing' => FALSE]]));
 
-    $generator = new UrlGenerator($provider->reveal(), $path_processor->reveal(), $this->routeProcessorManager, $this->requestStack, ['http', 'https']);
+    $generator = new UrlGenerator($provider->reveal(), $path_processor->reveal(), $this->routeProcessorManager, $this->requestStack, $route_preloader->reveal(), ['http', 'https']);
     $generator->setContext($this->context);
 
     $url = $generator->generateFromRoute('test_1', []);
@@ -269,11 +284,12 @@ public function testUrlGenerationWithDisabledPathProcessingByRoute() {
   public function testUrlGenerationWithDisabledPathProcessingByRouteAndOptedInPathProcessing() {
     $path_processor = $this->prophesize(OutboundPathProcessorInterface::class);
     $path_processor->processOutbound('/test/one', Argument::cetera())->willReturn('/hello/world')->shouldBeCalled();
+    $route_preloader = $this->prophesize(RoutePreloader::class);
 
     $provider = $this->prophesize(RouteProviderInterface::class);
     $provider->getRouteByName('test_1')->willReturn(new Route('/test/one', [], [], ['default_url_options' => ['path_processing' => FALSE]]));
 
-    $generator = new UrlGenerator($provider->reveal(), $path_processor->reveal(), $this->routeProcessorManager, $this->requestStack, ['http', 'https']);
+    $generator = new UrlGenerator($provider->reveal(), $path_processor->reveal(), $this->routeProcessorManager, $this->requestStack, $route_preloader->reveal(), ['http', 'https']);
     $generator->setContext($this->context);
 
     $url = $generator->generateFromRoute('test_1', [], ['path_processing' => TRUE]);
