diff --git a/core/misc/autocomplete.js b/core/misc/autocomplete.js
index 70ccf0be5170fbe392026fd3b4b280be7d42f907..639f9dc18e5939cde9587efdc7ad1318322e870d 100644
--- a/core/misc/autocomplete.js
+++ b/core/misc/autocomplete.js
@@ -59,9 +59,14 @@
    * @return {Boolean}
    */
   function searchHandler(event) {
-    // Only search when the term is two characters or larger.
+    var options = autocomplete.options;
     var term = autocomplete.extractLastTerm(event.target.value);
-    return term.length >= autocomplete.minLength;
+    // Abort search if the first character is in firstCharacterBlacklist.
+    if (term.length > 0 && options.firstCharacterBlacklist.indexOf(term[0]) !== -1) {
+      return false;
+    }
+    // Only search when the term is at least the minimum length.
+    return term.length >= options.minLength;
   }
 
   /**
@@ -174,6 +179,11 @@
       // Act on textfields with the "form-autocomplete" class.
       var $autocomplete = $(context).find('input.form-autocomplete').once('autocomplete');
       if ($autocomplete.length) {
+        // Allow options to be overriden per instance.
+        var blacklist = $autocomplete.attr('data-autocomplete-first-character-blacklist');
+        $.extend(autocomplete.options, {
+          firstCharacterBlacklist: (blacklist) ? blacklist : ''
+        });
         // Use jQuery UI Autocomplete on the textfield.
         $autocomplete.autocomplete(autocomplete.options)
           .data("ui-autocomplete")
@@ -194,8 +204,7 @@
    */
   autocomplete = {
     cache: {},
-    // Exposes methods to allow overriding by contrib.
-    minLength: 1,
+    // Exposes options to allow overriding by contrib.
     splitValues: autocompleteSplitValues,
     extractLastTerm: extractLastTerm,
     // jQuery UI autocomplete options.
@@ -204,7 +213,10 @@
       focus: focusHandler,
       search: searchHandler,
       select: selectHandler,
-      renderItem: renderItem
+      renderItem: renderItem,
+      minLength: 1,
+      // Custom options, used by Drupal.autocomplete.
+      firstCharacterBlacklist: ''
     },
     ajax: {
       dataType: 'json'
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index 2a25702502f51b2cdeaaa11dfd8310144bf82c7d..169204a4af6f7951c1b021f50f0f5e25c84079ab 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -9,11 +9,13 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Entity\Element\EntityAutocomplete;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Field\WidgetBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
 use Drupal\link\LinkItemInterface;
+use Symfony\Component\Routing\Exception\RouteNotFoundException;
 use Symfony\Component\Validator\ConstraintViolation;
 use Symfony\Component\Validator\ConstraintViolationInterface;
 use Symfony\Component\Validator\ConstraintViolationListInterface;
@@ -42,83 +44,101 @@ public static function defaultSettings() {
   }
 
   /**
-   * Gets the URI without the 'user-path:' scheme, for display while editing.
+   * Gets the URI without the 'user-path:' or 'entity:' scheme.
+   *
+   * The following two forms of URIs are transformed:
+   * - 'entity:' URIs: to entity autocomplete ("label (entity id)") strings;
+   * - 'user-path:' URIs: the scheme is stripped.
+   *
+   * This method is the inverse of ::getUserEnteredStringAsUri().
    *
    * @param string $uri
    *   The URI to get the displayable string for.
    *
    * @return string
+   *
+   * @see static::getUserEnteredStringAsUri()
    */
   protected static function getUriAsDisplayableString($uri) {
     $scheme = parse_url($uri, PHP_URL_SCHEME);
+
+    // By default, the displayable string is the URI.
+    $displayable_string = $uri;
+
+    // A different displayable string may be chosen in case of the 'user-path:'
+    // or 'entity:' built-in schemes.
     if ($scheme === 'user-path') {
       $uri_reference = explode(':', $uri, 2)[1];
-      // @todo Present the leading slash to the user and hence delete the next
-      //   block in https://www.drupal.org/node/2418017. There, we will also
-      //   remove the ability to enter '<front>' or '<none>', we'll expect '/'
-      //   and '' instead respectively.
+
+      // @todo '<front>' is valid input for BC reasons, may be removed by
+      //   https://www.drupal.org/node/2421941
       $path = parse_url($uri, PHP_URL_PATH);
       if ($path === '/') {
         $uri_reference = '<front>' . substr($uri_reference, 1);
       }
-      elseif (empty($path)) {
-        $uri_reference = '<none>' . $uri_reference;
-      }
-      else {
-        $uri_reference = ltrim($uri_reference, '/');
-      }
+
+      $displayable_string = $uri_reference;
     }
-    else {
-      $uri_reference = $uri;
+    elseif ($scheme === 'entity') {
+      list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2);
+      // Show the 'entity:' URI as the entity autocomplete would, but only if:
+      // - the entity could be loaded, and;
+      // - the current user is allowed to view the entity (otherwise we have a
+      //   information disclosure security problem).
+      $entity_manager = \Drupal::entityManager();
+      if ($entity_manager->getDefinition($entity_type, FALSE)) {
+        $entity = \Drupal::entityManager()->getStorage($entity_type)->load($entity_id);
+        if ($entity) {
+          $label = ($entity->access('view')) ? $entity->label() : t('- Restricted access -');
+          $displayable_string = $label . ' (' . $entity_id . ')';
+        }
+      }
     }
-    return $uri_reference;
+
+    return $displayable_string;
   }
 
   /**
    * Gets the user-entered string as a URI.
    *
-   * Schemeless URIs are treated as 'user-path:' URIs.
+   * The following two forms of input are mapped to URIs:
+   * - entity autocomplete ("label (entity id)") strings: to 'entity:' URIs;
+   * - strings without a detectable scheme: to 'user-path:' URIs.
+   *
+   * This method is the inverse of ::getUriAsDisplayableString().
    *
    * @param string $string
    *   The user-entered string.
    *
    * @return string
-   *   The URI, if a non-empty $string was passed.
+   *   The URI, if a non-empty $uri was passed.
+   *
+   * @see static::getUriAsDisplayableString()
    */
   protected static function getUserEnteredStringAsUri($string) {
-    if (!empty($string)) {
-      // Users can enter relative URLs, but we need a valid URI, so add an
-      // explicit scheme when necessary.
-      if (parse_url($string, PHP_URL_SCHEME) === NULL) {
-        // @todo Present the leading slash to the user and hence delete the next
-        //   block in https://www.drupal.org/node/2418017. There, we will also
-        //   remove the ability to enter '<front>' or '<none>', we'll expect '/'
-        //   and '' instead respectively.
-        // Users can enter paths that don't start with a leading slash, we
-        // want to normalize them to have a leading slash. However, we don't
-        // want to add a leading slash if it already starts with one, or if it
-        // contains only a querystring or a fragment. Examples:
-        // - 'foo' -> '/foo'
-        // - '?foo=bar' -> '/?foo=bar'
-        // - '#foo' -> '/#foo'
-        // - '<front>' -> '/'
-        // - '<front>#foo' -> '/#foo'
-        // - '<none>' -> ''
-        // - '<none>#foo' -> '#foo'
-        if (strpos($string, '<front>') === 0) {
-          $string = '/' . substr($string, strlen('<front>'));
-        }
-        elseif (strpos($string, '<none>') === 0) {
-          $string = substr($string, strlen('<none>'));
-        }
-        elseif (!in_array($string[0], ['/', '?', '#'])) {
-          $string = '/' . $string;
-        }
-
-        return 'user-path:' . $string;
+    // By default, assume the entered string is an URI.
+    $uri = $string;
+
+    // Detect entity autocomplete string, map to 'entity:' URI.
+    $entity_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($string);
+    if ($entity_id !== NULL) {
+      // @todo Support entity types other than 'node'. Will be fixed in
+      //    https://www.drupal.org/node/2423093.
+      $uri = 'entity:node/' . $entity_id;
+    }
+    // Detect a schemeless string, map to 'user-path:' URI.
+    elseif (!empty($string) && parse_url($string, PHP_URL_SCHEME) === NULL) {
+      // @todo '<front>' is valid input for BC reasons, may be removed by
+      //   https://www.drupal.org/node/2421941
+      // - '<front>' -> '/'
+      // - '<front>#foo' -> '/#foo'
+      if (strpos($string, '<front>') === 0) {
+        $string = '/' . substr($string, strlen('<front>'));
       }
+      $uri = 'user-path:' . $string;
     }
-    return $string;
+
+    return $uri;
   }
 
   /**
@@ -126,6 +146,15 @@ protected static function getUserEnteredStringAsUri($string) {
    */
   public static function validateUriElement($element, FormStateInterface $form_state, $form) {
     $uri = static::getUserEnteredStringAsUri($element['#value']);
+    $form_state->setValueForElement($element, $uri);
+
+    // If getUserEnteredStringAsUri() mapped the entered value is mapped to a
+    // 'user-path:' URI , ensure the raw value begins with '/', '?' or '#'.
+    // @todo '<front>' is valid input for BC reasons, may be removed by
+    //   https://www.drupal.org/node/2421941
+    if (parse_url($uri, PHP_URL_SCHEME) === 'user-path' && !in_array($element['#value'][0], ['/', '?', '#'], TRUE) && substr($element['#value'], 0, 7) !== '<front>') {
+      $form_state->setError($element, t('Manually entered paths should start with /, ? or #.'));
+    }
 
     // If the URI is empty or not well-formed, the link field type's validation
     // constraint will detect it.
@@ -140,6 +169,15 @@ public static function validateUriElement($element, FormStateInterface $form_sta
       $disallowed = $disallowed || (!\Drupal::currentUser()->hasPermission('link to any page') && !$url->access());
       // Disallow external URLs using untrusted protocols.
       $disallowed = $disallowed || ($url->isExternal() && !in_array(parse_url($uri, PHP_URL_SCHEME), UrlHelper::getAllowedProtocols()));
+      // Disallow routed URLs that don't exist.
+      if (!$disallowed && $url->isRouted()) {
+        try {
+          $url->toString();
+        }
+        catch (RouteNotFoundException $e) {
+          $disallowed = TRUE;
+        }
+      }
 
       if ($disallowed) {
         $form_state->setError($element, t("The path '@link_path' is either invalid or you do not have access to it.", ['@link_path' => static::getUriAsDisplayableString($uri)]));
@@ -170,18 +208,23 @@ public function formElement(FieldItemListInterface $items, $delta, array $elemen
     // If the field is configured to support internal links, it cannot use the
     // 'url' form element and we have to do the validation ourselves.
     if ($this->supportsInternalLinks()) {
-      $element['uri']['#type'] = 'textfield';
+      $element['uri']['#type'] = 'entity_autocomplete';
+      // @todo The user should be able to select an entity type. Will be fixed
+      //    in https://www.drupal.org/node/2423093.
+      $element['uri']['#target_type'] = 'node';
+      // Disable autocompletion when the first character is '/', '#' or '?'.
+      $element['uri']['#attributes']['data-autocomplete-first-character-blacklist'] = '/#?';
     }
 
     // If the field is configured to allow only internal links, add a useful
     // element prefix.
     if (!$this->supportsExternalLinks()) {
-      $element['uri']['#field_prefix'] = \Drupal::url('<front>', array(), array('absolute' => TRUE));
+      $element['uri']['#field_prefix'] = rtrim(\Drupal::url('<front>', array(), array('absolute' => TRUE)), '/');
     }
     // If the field is configured to allow both internal and external links,
     // show a useful description.
     elseif ($this->supportsExternalLinks() && $this->supportsInternalLinks()) {
-      $element['uri']['#description'] = $this->t('This can be an internal path such as %add-node or an external URL such as %url. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%url' => 'http://example.com'));
+      $element['uri']['#description'] = $this->t('Start typing the title of a piece of content to select it. You can also enter an internal path such as %add-node or an external URL such as %url. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => '/node/add', '%url' => 'http://example.com'));
     }
 
     $element['title'] = array(
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index 05a427e4747a0810d58267e6271daca46e195e2f..42ee4a3fd83719f3efe4f463feaa2b077195f3b1 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -25,7 +25,7 @@ class LinkFieldTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = ['entity_test', 'link'];
+  public static $modules = ['entity_test', 'link', 'node'];
 
   /**
    * A field to use in this test class.
@@ -93,27 +93,61 @@ function testURLValidation() {
 
     // Create a path alias.
     \Drupal::service('path.alias_storage')->save('admin', 'a/path/alias');
-    // Define some valid URLs.
+
+    // Create a node to test the link widget.
+    $node = $this->drupalCreateNode();
+
+    // Define some valid URLs (keys are the entered values, values are the
+    // strings displayed to the user).
     $valid_external_entries = array(
-      'http://www.example.com/',
+      'http://www.example.com/' => 'http://www.example.com/',
     );
     $valid_internal_entries = array(
-      'entity_test/add',
-      'a/path/alias',
-      'entity:user/1',
+      '/entity_test/add' => '/entity_test/add',
+      '/a/path/alias' => '/a/path/alias',
+
+      // Front page, with query string and fragment.
+      '/' => '&lt;front&gt;',
+      '/?example=llama' => '&lt;front&gt;?example=llama',
+      '/#example' => '&lt;front&gt;#example',
+
+      // @todo '<front>' is valid input for BC reasons, may be removed by
+      //   https://www.drupal.org/node/2421941
+      '<front>' => '&lt;front&gt;',
+      '<front>#example' => '&lt;front&gt;#example',
+      '<front>?example=llama' =>'&lt;front&gt;?example=llama',
+
+      // Query string and fragment.
+      '?example=llama' => '?example=llama',
+      '#example' => '#example',
+
+      // Entity reference autocomplete value.
+      $node->label() . ' (1)' => $node->label() . ' (1)',
+      // Entity URI displayed as ER autocomplete value when displayed in a form.
+      'entity:node/1' => $node->label() . ' (1)',
+      // URI for an entity that exists, but is not accessible by the user.
+      'entity:user/1' => '- Restricted access - (1)',
+      // URI for an entity that doesn't exist, but with a valid ID.
+      'entity:user/999999' => 'entity:user/999999',
+      // URI for an entity that doesn't exist, with an invalid ID.
+      'entity:user/invalid-parameter' => 'entity:user/invalid-parameter',
     );
 
     // Define some invalid URLs.
+    $validation_error_1 = "The path '@link_path' is either invalid or you do not have access to it.";
+    $validation_error_2 = 'Manually entered paths should start with /, ? or #.';
     $invalid_external_entries = array(
       // Missing protcol
-      'not-an-url',
+      'not-an-url' => $validation_error_2,
       // Invalid protocol
-      'invalid://not-a-valid-protocol',
+      'invalid://not-a-valid-protocol' => $validation_error_1,
       // Missing host name
-      'http://',
+      'http://' => $validation_error_1,
     );
     $invalid_internal_entries = array(
-      'non/existing/path',
+      '/non/existing/path' => $validation_error_1,
+      'no-leading-slash' => $validation_error_2,
+      'entity:non_existing_entity_type/yar' => $validation_error_1,
     );
 
     // Test external and internal URLs for 'link_type' = LinkItemInterface::LINK_GENERIC.
@@ -142,15 +176,15 @@ function testURLValidation() {
    *   An array of valid URL entries.
    */
   protected function assertValidEntries($field_name, array $valid_entries) {
-    foreach ($valid_entries as $value) {
+    foreach ($valid_entries as $uri => $string) {
       $edit = array(
-        "{$field_name}[0][uri]" => $value,
+        "{$field_name}[0][uri]" => $uri,
       );
       $this->drupalPostForm('entity_test/add', $edit, t('Save'));
       preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
       $id = $match[1];
       $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
-      $this->assertRaw($value);
+      $this->assertRaw($string);
     }
   }
 
@@ -163,12 +197,12 @@ protected function assertValidEntries($field_name, array $valid_entries) {
    *   An array of invalid URL entries.
    */
   protected function assertInvalidEntries($field_name, array $invalid_entries) {
-    foreach ($invalid_entries as $invalid_value) {
+    foreach ($invalid_entries as $invalid_value => $error_message) {
       $edit = array(
         "{$field_name}[0][uri]" => $invalid_value,
       );
       $this->drupalPostForm('entity_test/add', $edit, t('Save'));
-      $this->assertText(t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $invalid_value)));
+      $this->assertText(t($error_message, array('@link_path' => $invalid_value)));
     }
   }
 
diff --git a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
index 4d386185768a819363ef9b31226f40bc0aa70f93..59b045871aa390b0e9453eeb944f922498ff0ed1 100644
--- a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
@@ -65,7 +65,7 @@ function testMenuLanguage() {
     $this->assertOptionSelected('edit-langcode', $edit['langcode'], 'The menu language was correctly selected.');
 
     // Test menu link language.
-    $link_path = '<front>';
+    $link_path = '/';
 
     // Add a menu link.
     $link_title = $this->randomString();
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index a994bb4771a66a737cfdbbc146a9403f35aa3b59..d3897cdb0c729be831ea279d6f925f021aad69a0 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -266,7 +266,7 @@ function doMenuTests() {
 
     $this->clickLink(t('Add link'));
     $link_title = $this->randomString();
-    $this->drupalPostForm(NULL, array('link[0][uri]' => '<front>', 'title[0][value]' => $link_title), t('Save'));
+    $this->drupalPostForm(NULL, array('link[0][uri]' => '/', 'title[0][value]' => $link_title), t('Save'));
     $this->assertUrl(Url::fromRoute('entity.menu.edit_form',  ['menu' => $menu_name]));
     // Test the 'Edit' operation.
     $this->clickLink(t('Edit'));
@@ -301,9 +301,9 @@ function doMenuTests() {
     $this->doMenuLinkFormDefaultsTest();
 
     // Add menu links.
-    $item1 = $this->addMenuLink('', 'node/' . $node1->id(), $menu_name, TRUE);
-    $item2 = $this->addMenuLink($item1->getPluginId(), 'node/' . $node2->id(), $menu_name, FALSE);
-    $item3 = $this->addMenuLink($item2->getPluginId(), 'node/' . $node3->id(), $menu_name);
+    $item1 = $this->addMenuLink('', '/node/' . $node1->id(), $menu_name, TRUE);
+    $item2 = $this->addMenuLink($item1->getPluginId(), '/node/' . $node2->id(), $menu_name, FALSE);
+    $item3 = $this->addMenuLink($item2->getPluginId(), '/node/' . $node3->id(), $menu_name);
 
     // Hierarchy
     // <$menu_name>
@@ -337,10 +337,10 @@ function doMenuTests() {
     $this->verifyMenuLink($item3, $node3, $item2, $node2);
 
     // Add more menu links.
-    $item4 = $this->addMenuLink('', 'node/' . $node4->id(), $menu_name);
-    $item5 = $this->addMenuLink($item4->getPluginId(), 'node/' . $node5->id(), $menu_name);
+    $item4 = $this->addMenuLink('', '/node/' . $node4->id(), $menu_name);
+    $item5 = $this->addMenuLink($item4->getPluginId(), '/node/' . $node5->id(), $menu_name);
     // Create a menu link pointing to an alias.
-    $item6 = $this->addMenuLink($item4->getPluginId(), 'node5', $menu_name, TRUE, '0');
+    $item6 = $this->addMenuLink($item4->getPluginId(), '/node5', $menu_name, TRUE, '0');
 
     // Hierarchy
     // <$menu_name>
@@ -427,7 +427,7 @@ function doMenuTests() {
     // item's weight doesn't get changed because of the old hardcoded delta=50.
     $items = array();
     for ($i = -50; $i <= 51; $i++) {
-      $items[$i] = $this->addMenuLink('', 'node/' . $node1->id(), $menu_name, TRUE, strval($i));
+      $items[$i] = $this->addMenuLink('', '/node/' . $node1->id(), $menu_name, TRUE, strval($i));
     }
     $this->assertMenuLink($items[51]->getPluginId(), array('weight' => '51'));
 
@@ -454,7 +454,7 @@ function doMenuTests() {
     $this->assertMenuLink($item7->getPluginId(), array('url' => 'http://drupal.org'));
 
     // Add <front> menu item.
-    $item8 = $this->addMenuLink('', '<front>', $menu_name);
+    $item8 = $this->addMenuLink('', '/', $menu_name);
     $this->assertMenuLink($item8->getPluginId(), array('route_name' => '<front>'));
     $this->drupalGet('');
     $this->assertResponse(200);
@@ -494,20 +494,19 @@ function testMenuQueryAndFragment() {
     $this->drupalLogin($this->adminUser);
 
     // Make a path with query and fragment on.
-    $path = 'test-page?arg1=value1&arg2=value2';
+    $path = '/test-page?arg1=value1&arg2=value2';
     $item = $this->addMenuLink('', $path);
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
     $this->assertFieldByName('link[0][uri]', $path, 'Path is found with both query and fragment.');
 
     // Now change the path to something without query and fragment.
-    $path = 'test-page';
+    $path = '/test-page';
     $this->drupalPostForm('admin/structure/menu/item/' . $item->id() . '/edit', array('link[0][uri]' => $path), t('Save'));
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
     $this->assertFieldByName('link[0][uri]', $path, 'Path no longer has query or fragment.');
 
-    // Use <front>#fragment and ensure that saving it does not loose its
-    // content.
+    // Use <front>#fragment and ensure that saving it does not lose its content.
     $path = '<front>?arg1=value#fragment';
     $item = $this->addMenuLink('', $path);
 
@@ -547,7 +546,7 @@ function testUnpublishedNodeMenuItem() {
       'status' => NODE_NOT_PUBLISHED,
     ));
 
-    $item = $this->addMenuLink('', 'node/' . $node->id());
+    $item = $this->addMenuLink('', '/node/' . $node->id());
     $this->modifyMenuLink($item);
 
     // Test that a user with 'administer menu' but without 'bypass node access'
@@ -564,7 +563,7 @@ function testUnpublishedNodeMenuItem() {
   public function testBlockContextualLinks() {
     $this->drupalLogin($this->drupalCreateUser(array('administer menu', 'access contextual links', 'administer blocks')));
     $custom_menu = $this->addCustomMenu();
-    $this->addMenuLink('', '<front>', $custom_menu->id());
+    $this->addMenuLink('', '/', $custom_menu->id());
     $block = $this->drupalPlaceBlock('system_menu_block:' . $custom_menu->id(), array('label' => 'Custom menu', 'provider' => 'system'));
     $this->drupalGet('test-page');
 
@@ -606,7 +605,7 @@ public function testBlockContextualLinks() {
    * @return \Drupal\menu_link_content\Entity\MenuLinkContent
    *   A menu link entity.
    */
-  function addMenuLink($parent = '', $path = '<front>', $menu_name = 'tools', $expanded = FALSE, $weight = '0') {
+  function addMenuLink($parent = '', $path = '/', $menu_name = 'tools', $expanded = FALSE, $weight = '0') {
     // View add menu link page.
     $this->drupalGet("admin/structure/menu/manage/$menu_name/add");
     $this->assertResponse(200);
@@ -640,7 +639,7 @@ function addMenuLink($parent = '', $path = '<front>', $menu_name = 'tools', $exp
    * Attempts to add menu link with invalid path or no access permission.
    */
   function addInvalidMenuLink() {
-    foreach (array('-&-', 'admin/people/permissions') as $link_path) {
+    foreach (array('/-&-', '/admin/people/permissions') as $link_path) {
       $edit = array(
         'link[0][uri]' => $link_path,
         'title[0][value]' => 'title',
@@ -666,7 +665,7 @@ function checkInvalidParentMenuLinks() {
       $parent = $last_link ? 'tools:' . $last_link->getPluginId() : 'tools:';
       $title = 'title' . $i;
       $edit = array(
-        'link[0][uri]' => '<front>',
+        'link[0][uri]' => '/',
         'title[0][value]' => $title,
         'menu_parent' => $parent,
         'description[0][value]' => '',
diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
index 677e471d57b2b333359067d48d0d96a73c4482e1..47fbe8131861ea8520efad5933f5f698a4e7632a 100644
--- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -41,18 +41,18 @@ public function testShortcutLinkAdd() {
 
     // Create some paths to test.
     $test_cases = [
-      '<front>',
-      'admin',
-      'admin/config/system/site-information',
-      'node/' . $this->node->id() . '/edit',
-      $path['alias'],
-      'router_test/test2',
-      'router_test/test3/value',
+      '/',
+      '/admin',
+      '/admin/config/system/site-information',
+      '/node/' . $this->node->id() . '/edit',
+      '/' . $path['alias'],
+      '/router_test/test2',
+      '/router_test/test3/value',
     ];
 
     $test_cases_non_access = [
-      'admin',
-      'admin/config/system/site-information',
+      '/admin',
+      '/admin/config/system/site-information',
     ];
 
     // Check that each new shortcut links where it should.
@@ -66,7 +66,7 @@ public function testShortcutLinkAdd() {
       $this->assertResponse(200);
       $saved_set = ShortcutSet::load($set->id());
       $paths = $this->getShortcutInformation($saved_set, 'link');
-      $this->assertTrue(in_array('user-path:/' . ($test_path == '<front>' ? '' : $test_path), $paths), 'Shortcut created: ' . $test_path);
+      $this->assertTrue(in_array('user-path:' . $test_path, $paths), 'Shortcut created: ' . $test_path);
 
       if (in_array($test_path, $test_cases_non_access)) {
         $this->assertNoLink($title, String::format('Shortcut link %url not accessible on the page.', ['%url' => $test_path]));
@@ -92,15 +92,15 @@ public function testShortcutLinkAdd() {
     $title = $this->randomMachineName();
     $form_data = [
       'title[0][value]' => $title,
-      'link[0][uri]' => 'admin',
+      'link[0][uri]' => '/admin',
     ];
     $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
     $this->assertResponse(200);
-    $this->assertRaw(t("The path '@link_path' is either invalid or you do not have access to it.", ['@link_path' => 'admin']));
+    $this->assertRaw(t("The path '@link_path' is either invalid or you do not have access to it.", ['@link_path' => '/admin']));
 
     $form_data = [
       'title[0][value]' => $title,
-      'link[0][uri]' => 'node',
+      'link[0][uri]' => '/node',
     ];
     $this->drupalPostForm('admin/config/user-interface/shortcut/manage/' . $set->id() . '/add-link', $form_data, t('Save'));
     $this->assertLink($title, 0, 'Shortcut link found on the page.');
@@ -147,7 +147,7 @@ public function testShortcutLinkRename() {
 
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
-    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $new_link_name, 'link[0][uri]' => $shortcut->link->uri), t('Save'));
+    $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $new_link_name), t('Save'));
     $saved_set = ShortcutSet::load($set->id());
     $titles = $this->getShortcutInformation($saved_set, 'title');
     $this->assertTrue(in_array($new_link_name, $titles), 'Shortcut renamed: ' . $new_link_name);
@@ -161,14 +161,14 @@ public function testShortcutLinkChangePath() {
     $set = $this->set;
 
     // Tests changing a shortcut path.
-    $new_link_path = 'admin/config';
+    $new_link_path = '/admin/config';
 
     $shortcuts = $set->getShortcuts();
     $shortcut = reset($shortcuts);
     $this->drupalPostForm('admin/config/user-interface/shortcut/link/' . $shortcut->id(), array('title[0][value]' => $shortcut->getTitle(), 'link[0][uri]' => $new_link_path), t('Save'));
     $saved_set = ShortcutSet::load($set->id());
     $paths = $this->getShortcutInformation($saved_set, 'link');
-    $this->assertTrue(in_array('user-path:/' . $new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
+    $this->assertTrue(in_array('user-path:' . $new_link_path, $paths), 'Shortcut path changed: ' . $new_link_path);
     $this->assertLinkByHref($new_link_path, 0, 'Shortcut with new path appears on the page.');
   }
 
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index ab06c99f87330cd3ca2c7364e16a57260e2483ad..175509197b9a77f419c039b4795e0207e11a882f 100644
--- a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
@@ -187,7 +187,7 @@ function testBreadCrumbs() {
     $menu = 'tools';
     $edit = array(
       'title[0][value]' => 'Root',
-      'link[0][uri]' => 'node',
+      'link[0][uri]' => '/node',
     );
     $this->drupalPostForm("admin/structure/menu/manage/$menu/add", $edit, t('Save'));
     $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => 'Root'));
@@ -240,7 +240,7 @@ function testBreadCrumbs() {
       $term = $data['term'];
       $edit = array(
         'title[0][value]' => "$name link",
-        'link[0][uri]' => "taxonomy/term/{$term->id()}",
+        'link[0][uri]' => "/taxonomy/term/{$term->id()}",
         'menu_parent' => "$menu:{$parent_mlid}",
         'enabled[value]' => 1,
       );
diff --git a/core/modules/system/system.routing.yml b/core/modules/system/system.routing.yml
index 7c87bb92ccb501ea8d16371f6b4628b83969f21c..fecfd249fa207d1d1232cefa2d980dc2774b6040 100644
--- a/core/modules/system/system.routing.yml
+++ b/core/modules/system/system.routing.yml
@@ -382,7 +382,8 @@ system.theme_settings_theme:
   path: ''
   options:
     _only_fragment: TRUE
-
+  requirements:
+    _access: 'TRUE'
 
 '<current>':
   path: '<current>'