diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php
index 5d92e77..18beb92 100644
--- a/core/lib/Drupal/Core/Routing/RouteProvider.php
+++ b/core/lib/Drupal/Core/Routing/RouteProvider.php
@@ -146,22 +146,23 @@ public function __construct(Connection $connection, StateInterface $state, Curre
    */
   public function getRouteCollectionForRequest(Request $request) {
     // Cache both the system path as well as route parameters and matching
-    // routes.
-    $cid = 'route:' . $request->getPathInfo() . ':' . $request->getQueryString();
+    // routes. We can not yet convert the path to lowercase since wildcard path
+    // portions may be case sensitive if they contain data like a base64 encoded
+    // token. We remove repeated and trailing slashes to normalize the path.
+    $parts = preg_split('@/+@', $request->getPathInfo(), NULL, PREG_SPLIT_NO_EMPTY);
+    $path = '/' . implode('/', $parts);
+    $cid = 'route:' . $path . ':' . $request->getQueryString();
     if ($cached = $this->cache->get($cid)) {
       $this->currentPath->setPath($cached->data['path'], $request);
       $request->query->replace($cached->data['query']);
       return $cached->data['routes'];
     }
     else {
-      // Just trim on the right side.
-      $path = $request->getPathInfo();
-      $path = $path === '/' ? $path : rtrim($request->getPathInfo(), '/');
       $path = $this->pathProcessor->processInbound($path, $request);
       $this->currentPath->setPath($path, $request);
       // Incoming path processors may also set query parameters.
       $query_parameters = $request->query->all();
-      $routes = $this->getRoutesByPath(rtrim($path, '/'));
+      $routes = $this->getRoutesByPath($path);
       $cache_value = [
         'path' => $path,
         'query' => $query_parameters,
diff --git a/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php b/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
index 3aaac14..4f75ebf 100644
--- a/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
+++ b/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
@@ -33,6 +33,9 @@ public function testMixedCasePaths() {
     $this->drupalGet('user/login');
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->pageTextMatches('/Log in/');
+    $this->drupalGet('user//login/');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/Log in/');
     $this->drupalGet('User/Login');
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->pageTextMatches('/Log in/');
@@ -85,6 +88,10 @@ public function testMixedCasePaths() {
     $this->drupalGet('NOdE/' . $node->id());
     $this->assertSession()->statusCodeEquals(200);
     $this->assertSession()->pageTextMatches('/FooBarBaz/');
+    // Check that we can access the node with a mixed case path and extra slash.
+    $this->drupalGet('Node//' . $node->id());
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/FooBarBaz/');
   }
 
   /**
diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
index ed59f2f..12ef36c 100644
--- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
@@ -250,7 +250,7 @@ public function testMixedCasePaths($path, $expected_route_name, $method = 'GET')
   }
 
   /**
-   * Data provider for testMixedCasePaths()
+   * Data provider for testDuplicateRoutePaths()
    */
   public function providerDuplicateRoutePaths() {
     // When matching routes with the same fit the route with the lowest-sorting
@@ -297,6 +297,50 @@ public function testDuplicateRoutePaths($path, $number, $expected_route_name = N
   }
 
   /**
+   * Data provider for testRepeatedSlashPaths()
+   */
+  public function providerRepeatedSlashPaths() {
+    // Note that paths with multiple leading slashes get redirected, and also
+    // cannot be used to create a Request object, so multiple leading slashes
+    // are not part of the text cases.
+    return [
+      ['/somewhere////987//over/the/rainbow', 'route_c'],
+      ['/somewhere/987/over//the///////rainbow', 'route_c'],
+      ['/another/432/about/llama//', 'route_d'],
+      ['/another//432////about////llama', 'route_d'],
+      ['/path//two', NULL],
+    ];
+  }
+
+  /**
+   * Confirms that repeated slashes in a request path are ignored for routing.
+   *
+   * @dataProvider providerRepeatedSlashPaths
+   */
+  public function testRepeatedSlashPaths($path, $expected_route_name) {
+    $connection = Database::getConnection();
+    $provider = new RouteProvider($connection, $this->state, $this->currentPath, $this->cache, $this->pathProcessor, $this->cacheTagsInvalidator, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $dumper = new MatcherDumper($connection, $this->state, 'test_routes');
+    $dumper->addRoutes($this->fixtures->complexRouteCollection());
+    $dumper->dump();
+
+    $request = Request::create($path);
+
+    $routes = $provider->getRouteCollectionForRequest($request);
+
+    if ($expected_route_name) {
+      $this->assertEquals(1, count($routes), 'The correct number of routes was found.');
+      $this->assertNotNull($routes->get($expected_route_name), 'The first matching route was found.');
+    }
+    else {
+      $this->assertEquals(0, count($routes), 'No routes matched.');
+    }
+  }
+
+  /**
    * Confirms that a trailing slash on the request does not result in a 404.
    */
   public function testOutlinePathMatchTrailingSlash() {
