diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index 937bf48..72694e7 100644
--- a/core/lib/Drupal/Core/Routing/RouteCompiler.php
+++ b/core/lib/Drupal/Core/Routing/RouteCompiler.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Routing;
 
+use Drupal\Component\Utility\Unicode;
 use Symfony\Component\Routing\RouteCompilerInterface;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCompiler as SymfonyRouteCompiler;
@@ -37,7 +38,8 @@ public static function compile(Route $route) {
     // The Drupal-specific compiled information.
     $stripped_path = static::getPathWithoutDefaults($route);
     $fit = static::getFit($stripped_path);
-    $pattern_outline = static::getPatternOutline($stripped_path);
+    // Store a lower-case pattern outline to enable case-insensitive matching.
+    $pattern_outline = Unicode::strtolower(static::getPatternOutline($stripped_path));
     // We count the number of parts including any optional trailing parts. This
     // allows the RouteProvider to filter candidate routes more efficiently.
     $num_parts = count(explode('/', trim($route->getPath(), '/')));
@@ -46,23 +48,28 @@ public static function compile(Route $route) {
       $fit,
       $pattern_outline,
       $num_parts,
-      // These are the Symfony compiled parts.
-      $symfony_compiled->getStaticPrefix(),
-      $symfony_compiled->getRegex(),
+      // These are the compiled parts Symfony uses in
+      // \Symfony\Component\Routing\Matcher\UrlMatcher::matchCollection().
+      // Set the static prefix to an empty string since it is redundant to
+      // the matching in \Drupal\Core\Routing\RouteProvider::getRoutesByPath()
+      // and by skipping it we more easily make the routing case insensitive.
+      '',
+      // Set the regex to use UTF-8 and be case-insensitive.
+      $symfony_compiled->getRegex() . 'ui',
       $symfony_compiled->getTokens(),
       $symfony_compiled->getPathVariables(),
       $symfony_compiled->getHostRegex(),
       $symfony_compiled->getHostTokens(),
       $symfony_compiled->getHostVariables(),
       $symfony_compiled->getVariables()
-      );
+    );
   }
 
   /**
    * Returns the pattern outline.
    *
    * The pattern outline is the path pattern but normalized so that all
-   * placeholders are equal strings and default values are removed.
+   * placeholders are the string '%'.
    *
    * @param string $path
    *   The path for which we want the normalized outline.
diff --git a/core/lib/Drupal/Core/Routing/RouteProvider.php b/core/lib/Drupal/Core/Routing/RouteProvider.php
index 53ed626..aa20f7e 100644
--- a/core/lib/Drupal/Core/Routing/RouteProvider.php
+++ b/core/lib/Drupal/Core/Routing/RouteProvider.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Cache\CacheTagsInvalidatorInterface;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Path\CurrentPathStack;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\State\StateInterface;
@@ -143,22 +144,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,
@@ -317,15 +319,17 @@ public function getRoutesByPattern($pattern) {
    * Get all routes which match a certain pattern.
    *
    * @param string $path
-   *   The route pattern to search for (contains % as placeholders).
+   *   The route pattern to search for.
    *
    * @return \Symfony\Component\Routing\RouteCollection
    *   Returns a route collection of matching routes.
    */
   protected function getRoutesByPath($path) {
     // Split the path up on the slashes, ignoring multiple slashes in a row
-    // or leading or trailing slashes.
-    $parts = preg_split('@/+@', $path, NULL, PREG_SPLIT_NO_EMPTY);
+    // or leading or trailing slashes. Convert to lower case here so we can
+    // have a case insensitive match from the incoming path to the lower case
+    // pattern outlines from \Drupal\Core\Routing\RouteCompiler::compile().
+    $parts = preg_split('@/+@', Unicode::strtolower($path), NULL, PREG_SPLIT_NO_EMPTY);
 
     $collection = new RouteCollection();
 
diff --git a/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php b/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
new file mode 100644
index 0000000..83c58b5
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
@@ -0,0 +1,36 @@
+<?php
+
+namespace Drupal\FunctionalTests\Routing;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests url generation and routing for route paths with encoded characters.
+ *
+ * @group routing
+ */
+class CaseInsensitivePathTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'views', 'node'];
+
+  public function testInternalPath() {
+    $this->drupalGet('user/login');
+    $this->assertEquals($this->getSession()->getStatusCode(), 200);
+    $this->drupalGet('User/Login');
+    $this->assertEquals($this->getSession()->getStatusCode(), 200);
+  }
+
+  public function testViewsPath() {
+    $admin = $this->drupalCreateUser(['access administration pages', 'administer nodes', 'access content overview']);
+    $this->drupalLogin($admin);
+
+    $this->drupalGet('admin/content');
+    $this->assertEquals($this->getSession()->getStatusCode(), 200);
+    $this->drupalGet('Admin/Content');
+    $this->assertEquals($this->getSession()->getStatusCode(), 200);
+  }
+
+}
