diff --git a/core/lib/Drupal/Core/Routing/RouteCompiler.php b/core/lib/Drupal/Core/Routing/RouteCompiler.php
index 937bf48..52d0041 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,30 @@ public static function compile(Route $route) {
       $fit,
       $pattern_outline,
       $num_parts,
-      // These are the Symfony compiled parts.
-      $symfony_compiled->getStaticPrefix(),
-      $symfony_compiled->getRegex(),
+
+      // The following parameters are what 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..d283243 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;
@@ -317,15 +318,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/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
index eb8709e..8ef6457 100644
--- a/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
+++ b/core/modules/system/tests/modules/system_test/src/Controller/SystemTestController.php
@@ -313,6 +313,21 @@ public function configureTitle($foo) {
   }
 
   /**
+   * Simple argument echo.
+   *
+   * @param string $text
+   *   Any string for the {text} slug.
+   *
+   * @return array
+   *   A render array.
+   */
+  public function simpleEcho($text) {
+    return [
+      '#plain_text' => $text,
+    ];
+  }
+
+  /**
    * Shows permission-dependent content.
    *
    * @return array
diff --git a/core/modules/system/tests/modules/system_test/system_test.routing.yml b/core/modules/system/tests/modules/system_test/system_test.routing.yml
index f35f546..579b511 100644
--- a/core/modules/system/tests/modules/system_test/system_test.routing.yml
+++ b/core/modules/system/tests/modules/system_test/system_test.routing.yml
@@ -182,3 +182,17 @@ system_test.header:
     _controller: '\Drupal\system_test\Controller\SystemTestController::getTestHeader'
   requirements:
     _access: 'TRUE'
+
+system_test.echo:
+  path: '/system-test/echo/{text}'
+  defaults:
+    _controller: '\Drupal\system_test\Controller\SystemTestController::simpleEcho'
+  requirements:
+    _access: 'TRUE'
+
+system_test.echo_utf8:
+  path: '/system-test/Ȅchȏ/meφΩ/{text}'
+  defaults:
+    _controller: '\Drupal\system_test\Controller\SystemTestController::simpleEcho'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php b/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
new file mode 100644
index 0000000..183d140
--- /dev/null
+++ b/core/tests/Drupal/FunctionalTests/Routing/CaseInsensitivePathTest.php
@@ -0,0 +1,113 @@
+<?php
+
+namespace Drupal\FunctionalTests\Routing;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests incoming path case insensitivity.
+ *
+ * @group routing
+ */
+class CaseInsensitivePathTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['system', 'views', 'node', 'system_test'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+    \Drupal::state()->set('system_test.module_hidden', FALSE);
+    $this->createContentType(['type' => 'page']);
+  }
+
+  /**
+   * Tests mixed case paths.
+   */
+  public function testMixedCasePaths() {
+    // Tests paths defined by routes from standard modules as anonymous.
+    $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/');
+
+    // Tests paths defined by routes from the Views module.
+    $admin = $this->drupalCreateUser(['access administration pages', 'administer nodes', 'access content overview']);
+    $this->drupalLogin($admin);
+
+    $this->drupalGet('admin/content');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/Content/');
+    $this->drupalGet('Admin/Content');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/Content/');
+
+    // Tests paths with query arguments.
+
+    // Make sure our node title doesn't exist.
+    $this->drupalGet('admin/content');
+    $this->assertSession()->linkNotExists('FooBarBaz');
+    $this->assertSession()->linkNotExists('foobarbaz');
+
+    // Create a node, and make sure it shows up on admin/content.
+    $node = $this->createNode([
+      'title' => 'FooBarBaz',
+      'type' => 'page',
+    ]);
+
+    $this->drupalGet('admin/content', [
+      'query' => [
+        'title' => 'FooBarBaz'
+      ]
+    ]);
+
+    $this->assertSession()->linkExists('FooBarBaz');
+    $this->assertSession()->linkByHrefExists($node->toUrl()->toString());
+
+    // Make sure the path is case insensitive, and query case is preserved.
+
+    $this->drupalGet('Admin/Content', [
+      'query' => [
+        'title' => 'FooBarBaz'
+      ]
+    ]);
+
+    $this->assertSession()->linkExists('FooBarBaz');
+    $this->assertSession()->linkByHrefExists($node->toUrl()->toString());
+    $this->assertSession()->fieldValueEquals('edit-title', 'FooBarBaz');
+    // Check that we can access the node with a mixed case path.
+    $this->drupalGet('NOdE/' . $node->id());
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/FooBarBaz/');
+  }
+
+  /**
+   * Tests paths with slugs.
+   */
+  public function testPathsWithArguments() {
+    $this->drupalGet('system-test/echo/foobarbaz');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/foobarbaz/');
+    $this->assertSession()->pageTextNotMatches('/FooBarBaz/');
+
+    $this->drupalGet('system-test/echo/FooBarBaz');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/FooBarBaz/');
+    $this->assertSession()->pageTextNotMatches('/foobarbaz/');
+
+    // Test utf-8 characters in the route path.
+    $this->drupalGet('/system-test/Ȅchȏ/meΦω/ABc123');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/ABc123/');
+    $this->drupalGet('/system-test/ȅchȎ/MEΦΩ/ABc123');
+    $this->assertSession()->statusCodeEquals(200);
+    $this->assertSession()->pageTextMatches('/ABc123/');
+  }
+
+}
diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
index fa135f0..4c05894 100644
--- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
+++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\KernelTests\Core\Routing;
 
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Cache\MemoryBackend;
 use Drupal\Core\Database\Database;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
@@ -194,8 +195,63 @@ function testOutlinePathMatch() {
     $this->assertNotNull($routes->get('route_b'), 'The second matching route was not found.');
   }
 
+
+  /**
+   * Data provider for testMixedCasePaths()
+   */
+  public function providerMixedCaseRoutePaths() {
+    return [
+      ['/path/one', 'route_a'],
+      ['/path/two', NULL],
+      ['/PATH/one', 'route_a'],
+      ['/path/2/one', 'route_b', 'PUT'],
+      ['/paTH/3/one', 'route_b', 'PUT'],
+      // There should be no lower case of a Hebrew letter.
+      ['/somewhere/4/over/the/קainbow', 'route_c'],
+      ['/Somewhere/5/over/the/קainboW', 'route_c'],
+      ['/another/llama/aboUT/22', 'route_d'],
+      ['/another/llama/about/22', 'route_d'],
+      ['/place/meΦω', 'route_e', 'HEAD'],
+      ['/place/meφΩ', 'route_e', 'HEAD'],
+    ];
+  }
+
+  /**
+   * Confirms that we find routes using a case insensitive path match.
+   *
+   * @dataProvider providerMixedCaseRoutePaths
+   */
+  public function testMixedCasePaths($path, $expected_route_name, $method = 'GET') {
+    // The case-insensitive behavior for higher UTF-8 characters depends on
+    // \Drupal\Component\Utility\Unicode::strtolower() using mb_strtolower()
+    // but kernel tests do not currently run the check that enables it.
+    // @todo remove this when https://www.drupal.org/node/2849669 is fixed.
+    Unicode::check();
+
+    $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->mixedCaseRouteCollection());
+    $dumper->dump();
+
+    $request = Request::create($path, $method);
+
+    $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 doesn't result in a 404.
+   * Confirms that a trailing slash on the request does not result in a 404.
    */
   function testOutlinePathMatchTrailingSlash() {
     $connection = Database::getConnection();
diff --git a/core/tests/Drupal/Tests/Core/Routing/RoutingFixtures.php b/core/tests/Drupal/Tests/Core/Routing/RoutingFixtures.php
index d1826ed..1b7dec5 100644
--- a/core/tests/Drupal/Tests/Core/Routing/RoutingFixtures.php
+++ b/core/tests/Drupal/Tests/Core/Routing/RoutingFixtures.php
@@ -140,6 +140,38 @@ public function complexRouteCollection() {
   }
 
   /**
+   * Returns a complex set of routes for testing.
+   *
+   * @return \Symfony\Component\Routing\RouteCollection
+   */
+  public function mixedCaseRouteCollection() {
+    $collection = new RouteCollection();
+
+    $route = new Route('/path/one');
+    $route->setMethods(['GET']);
+    $collection->add('route_a', $route);
+
+    $route = new Route('/path/{thing}/one');
+    $route->setMethods(['PUT']);
+    $collection->add('route_b', $route);
+
+    // Uses Hewbrew letter QOF (U+05E7)
+    $route = new Route('/somewhere/{item}/over/the/קainbow');
+    $route->setMethods(['GET']);
+    $collection->add('route_c', $route);
+
+    $route = new Route('/another/{thing}/aboUT/{item}');
+    $collection->add('route_d', $route);
+
+    // Greek letters lower case phi (U+03C6) and lower case omega (U+03C9)
+    $route = new Route('/place/meφω');
+    $route->setMethods(['GET', 'HEAD']);
+    $collection->add('route_e', $route);
+
+    return $collection;
+  }
+
+  /**
    * Returns a Content-type restricted set of routes for testing.
    *
    * @return \Symfony\Component\Routing\RouteCollection
