diff --git a/core/lib/Drupal/Core/Path/PathValidator.php b/core/lib/Drupal/Core/Path/PathValidator.php
index 51cdc2f..fcf0968 100644
--- a/core/lib/Drupal/Core/Path/PathValidator.php
+++ b/core/lib/Drupal/Core/Path/PathValidator.php
@@ -109,6 +109,9 @@ protected function getUrl($path, $access_check) {
     if ($parsed_url['path'] == '<front>') {
       return new Url('<front>', [], $options);
     }
+    elseif ($parsed_url['path'] == '<none>') {
+      return new Url('<none>', [], $options);
+    }
     elseif (UrlHelper::isExternal($path) && UrlHelper::isValid($path)) {
       if (empty($parsed_url['path'])) {
         return FALSE;
diff --git a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
index e92fe53..680979a 100644
--- a/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
+++ b/core/modules/link/src/Plugin/Field/FieldWidget/LinkWidget.php
@@ -9,6 +9,7 @@
 
 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;
@@ -44,15 +45,31 @@ public static function defaultSettings() {
   /**
    * Gets the URI without the 'user-path:' scheme, for display while editing.
    *
+   * This method is the inverse of ::getUserEnteredStringAsUri().
+   *
    * @param string $uri
    *   The URI to get the displayable string for.
    *
    * @return string
+   *
+   * @see getUserEnteredStringAsUri
    */
   protected static function getUriAsDisplayableString($uri) {
     $scheme = parse_url($uri, PHP_URL_SCHEME);
+    $uri = str_replace('<none>', '', $uri);
+    $uri = str_replace('<front>', '/', $uri);
+
     if ($scheme === 'user-path') {
       $uri_reference = explode(':', $uri, 2)[1];
+      // Add / in case we don't start with ?, # and / already, which are the
+      // allowed characters to start with.
+      if (!($uri_reference && in_array($uri_reference[0], ['?', '#', '/']))) {
+        $uri_reference = '/' . $uri_reference;
+      }
+    }
+    elseif ($scheme === 'entity') {
+      list($entity_type, $entity_id) = explode('/', substr($uri, 7), 2);
+      $uri_reference = \Drupal::entityManager()->getStorage($entity_type)->load($entity_id)->label() . ' (' . $entity_id . ')';
     }
     else {
       $uri_reference = $uri;
@@ -65,18 +82,33 @@ protected static function getUriAsDisplayableString($uri) {
    *
    * Schemeless URIs are treated as '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 getUriAsDisplayableString
    */
   protected static function getUserEnteredStringAsUri($string) {
+    $entity_id = EntityAutocomplete::extractEntityIdFormAutocompletionResult($string);
+    if ($entity_id !== NULL) {
+      $string = 'entity:node/' . $entity_id;
+    }
+
     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) {
-        return 'user-path:' . $string;
+        if (parse_url($string, PHP_URL_PATH) === NULL) {
+          $string = '<none>' . $string;
+        }
+        if (parse_url($string, PHP_URL_PATH) === '/') {
+          $string = '<front>' . ltrim($string, '/');
+        }
+        $string = 'user-path:' . $string;
       }
     }
     return $string;
@@ -87,6 +119,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);
+
+    // Figure out the user entered value after user-path. This might contain a
+    // fragment so we cannot use parse_url($string, PHP_URL_PATH), so we strip
+    // out the 10 chars of user-path:.
+    if (parse_url($uri, PHP_URL_SCHEME) === 'user-path' && !in_array(substr($uri, 10)[0], ['#', '/', '?', '<'], TRUE)) {
+      //
+      $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.
@@ -131,18 +172,20 @@ 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 This should be a setting?
+      $element['uri']['#target_type'] = 'node';
     }
 
     // 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 Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => 'node/add', '%drupal' => 'http://drupal.org'));
+      $element['uri']['#description'] = $this->t('This can be an internal Drupal path such as %add-node or an external URL such as %drupal. Enter %front to link to the front page.', array('%front' => '<front>', '%add-node' => '/node/add', '%drupal' => 'http://drupal.org'));
     }
 
     $element['title'] = array(
diff --git a/core/modules/link/src/Tests/LinkFieldTest.php b/core/modules/link/src/Tests/LinkFieldTest.php
index 05a427e..e9abd29 100644
--- a/core/modules/link/src/Tests/LinkFieldTest.php
+++ b/core/modules/link/src/Tests/LinkFieldTest.php
@@ -98,9 +98,11 @@ function testURLValidation() {
       'http://www.example.com/',
     );
     $valid_internal_entries = array(
-      'entity_test/add',
-      'a/path/alias',
+      '/entity_test/add',
+      '/a/path/alias',
       'entity:user/1',
+      '#example',
+      '?example=llama',
     );
 
     // Define some invalid URLs.
@@ -113,7 +115,10 @@ function testURLValidation() {
       'http://',
     );
     $invalid_internal_entries = array(
-      'non/existing/path',
+      // No existing.
+      '/non/existing/path',
+      // Missing start with /
+      'no-slash-start',
     );
 
     // Test external and internal URLs for 'link_type' = LinkItemInterface::LINK_GENERIC.
@@ -150,6 +155,12 @@ protected function assertValidEntries($field_name, array $valid_entries) {
       preg_match('|entity_test/manage/(\d+)|', $this->url, $match);
       $id = $match[1];
       $this->assertText(t('entity_test @id has been created.', array('@id' => $id)));
+      // Special case entity URLs.
+      if (parse_url($value, PHP_URL_SCHEME) === 'entity') {
+        list($entity_type, $entity_id) = explode('/', parse_url($value, PHP_URL_PATH));
+        $entity = \Drupal::entityManager()->getStorage($entity_type)->load($entity_id);
+        $value = $entity->label() . ' (' . $entity_id . ')';
+      }
       $this->assertRaw($value);
     }
   }
@@ -168,7 +179,20 @@ protected function assertInvalidEntries($field_name, array $invalid_entries) {
         "{$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)));
+
+      // There are two types of invalid entries: Either the path does not exist/
+      // the user does not have access to it, OR the slash at the beginning is
+      // missing.
+      // If a schema is specified there is nothing to see, its kinda valid.
+      if (parse_url($invalid_value, PHP_URL_SCHEME)) {
+        $this->assertText(t("The path '@link_path' is either invalid or you do not have access to it.", array('@link_path' => $invalid_value)));
+      }
+      elseif (($path = parse_url($invalid_value, PHP_URL_PATH)) && !in_array($path[0], ['?', '#', '/'])) {
+        $this->assertText(t('Manually entered paths should start with /, # or ?'));
+      }
+      else {
+        $this->assertText(t("The path '@link_path' is either invalid or you do not have access to it.", 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 67411f2..cfd0e3e 100644
--- a/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuLanguageTest.php
@@ -70,7 +70,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 8763132..3b46210 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -106,7 +106,7 @@ function testMenu() {
 
     foreach ($this->items as $item) {
       // Paths were set as 'node/$nid'.
-      $node = Node::load(str_replace('user-path:node/', '', $item->link->uri));
+      $node = Node::load(str_replace('user-path:/node/', '', $item->link->uri));
       $this->verifyMenuLink($item, $node);
     }
 
@@ -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);
@@ -491,21 +491,21 @@ function testMenuQueryAndFragment() {
     $this->drupalLogin($this->admin_user);
 
     // 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.
-    $path = '<front>?arg1=value#fragment';
+    $path = '?arg1=value#fragment';
     $item = $this->addMenuLink('', $path);
 
     $this->drupalGet('admin/structure/menu/item/' . $item->id() . '/edit');
@@ -544,7 +544,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'
@@ -561,7 +561,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');
 
@@ -603,7 +603,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);
@@ -637,7 +637,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',
diff --git a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
index 40c230d..915d9df 100644
--- a/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
+++ b/core/modules/shortcut/src/Tests/ShortcutLinksTest.php
@@ -41,13 +41,13 @@ 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',
     ];
 
     // Check that each new shortcut links where it should.
@@ -61,7 +61,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, $paths), 'Shortcut created: ' . $test_path);
+      $this->assertTrue(in_array('user-path:' . ($test_path == '/' ? '<front>' : $test_path), $paths), 'Shortcut created: ' . $test_path);
       $this->assertLink($title, 0, String::format('Shortcut link %url found on the page.', ['%url' => $test_path]));
     }
     $saved_set = ShortcutSet::load($set->id());
@@ -81,15 +81,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.');
@@ -136,7 +136,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, 'link[0][uri]' => str_replace('user-path:', '', $shortcut->link->uri)), 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);
@@ -150,7 +150,7 @@ 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);
diff --git a/core/modules/system/src/Tests/Menu/BreadcrumbTest.php b/core/modules/system/src/Tests/Menu/BreadcrumbTest.php
index fff190b..1755091 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,
       );
@@ -248,7 +248,7 @@ function testBreadCrumbs() {
       $menu_links = entity_load_multiple_by_properties('menu_link_content', array(
         'title' => $edit['title[0][value]'],
         // @todo Use link.uri once https://www.drupal.org/node/2391217 is in.
-        'link__uri' => 'user-path:taxonomy/term/' . $term->id(),
+        'link__uri' => 'user-path:/taxonomy/term/' . $term->id(),
       ));
       $tags[$name]['link'] = reset($menu_links);
       $parent_mlid = $tags[$name]['link']->getPluginId();
