 core/lib/Drupal/Core/Entity/Entity.php             |   3 +
 .../Entity/Routing/DefaultHtmlRouteProvider.php    |  36 ++++++
 .../Core/Menu/DefaultMenuLinkTreeManipulators.php  |  47 ++++++--
 .../Drupal/Core/ParamConverter/EntityConverter.php |  17 ++-
 core/lib/Drupal/Core/Url.php                       |  12 +-
 core/modules/aggregator/src/Entity/Feed.php        |   1 +
 core/modules/menu_ui/menu_ui.module                |  16 ++-
 core/modules/menu_ui/src/Tests/MenuNodeTest.php    |  41 ++++++-
 core/modules/menu_ui/src/Tests/MenuTest.php        |  91 +--------------
 core/modules/menu_ui/src/Tests/MenuWebTestBase.php |  94 ++++++++++++++++
 core/modules/node/src/Entity/Node.php              |   1 +
 core/modules/node/src/Entity/NodeRouteProvider.php |  12 ++
 .../node/src/Tests/NodeAccessMenuLinkTest.php      |  40 ++++++-
 core/modules/system/system.install                 |  16 +++
 .../modules/entity_test/src/Entity/EntityTest.php  |   1 +
 .../src/Entity/EntityTestBaseFieldDisplay.php      |   1 +
 .../entity_test/src/Entity/EntityTestMul.php       |   1 +
 .../src/Entity/EntityTestMulChanged.php            |   1 +
 .../src/Entity/EntityTestMulLangcodeKey.php        |   1 +
 .../entity_test/src/Entity/EntityTestMulRev.php    |   1 +
 .../src/Entity/EntityTestMulRevChanged.php         |   1 +
 .../entity_test/src/Entity/EntityTestRev.php       |   1 +
 .../entity_test/src/Entity/EntityTestStringId.php  |   1 +
 .../src/Entity/EntityTestWithBundle.php            |   1 +
 .../src/Entity/EntityTestWithRevisionLog.php       |   1 +
 core/modules/taxonomy/src/Entity/Term.php          |   1 +
 core/modules/taxonomy/taxonomy.routing.yml         |  10 ++
 core/modules/user/src/Entity/User.php              |   1 +
 core/modules/user/src/Entity/UserRouteProvider.php |  12 ++
 .../src/Plugin/views/argument_validator/Entity.php |  22 +++-
 .../Unit/Plugin/argument_validator/EntityTest.php  |  11 +-
 .../Routing/DefaultHtmlRouteProviderTest.php       |  57 ++++++++++
 .../Menu/DefaultMenuLinkTreeManipulatorsTest.php   | 122 ++++++++++++++++++---
 .../Core/ParamConverter/EntityConverterTest.php    |   8 ++
 core/tests/Drupal/Tests/Core/UrlTest.php           |   8 ++
 35 files changed, 557 insertions(+), 133 deletions(-)

diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 2c6dbc1..b3c865e 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -315,6 +315,9 @@ protected function urlRouteParameters($rel) {
     if ($rel === 'revision' && $this instanceof RevisionableInterface) {
       $uri_route_parameters[$this->getEntityTypeId() . '_revision'] = $this->getRevisionId();
     }
+    if ($rel === 'uuid') {
+      $uri_route_parameters[$this->getEntityTypeId()] = $this->uuid();
+    }
 
     return $uri_route_parameters;
   }
diff --git a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
index 1ac54bb..d24035b 100644
--- a/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
+++ b/core/lib/Drupal/Core/Entity/Routing/DefaultHtmlRouteProvider.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Entity\Routing;
 
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
 use Drupal\Core\Entity\Controller\EntityController;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
@@ -24,6 +25,7 @@
  * - edit-form
  * - delete-form
  * - collection
+ * - uuid
  *
  * @see \Drupal\Core\Entity\Routing\AdminHtmlRouteProvider.
  *
@@ -84,6 +86,12 @@ public function getRoutes(EntityTypeInterface $entity_type) {
       $collection->add("entity.{$entity_type_id}.add_form", $add_form_route);
     }
 
+    // This goes before canonical because the UUID pattern must be tested before
+    // non-integer entity IDs.
+    if ($uuid_route = $this->getUuidRoute($entity_type)) {
+      $collection->add("entity.{$entity_type_id}.uuid", $uuid_route);
+    }
+
     if ($canonical_route = $this->getCanonicalRoute($entity_type)) {
       $collection->add("entity.{$entity_type_id}.canonical", $canonical_route);
     }
@@ -235,6 +243,34 @@ protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
   }
 
   /**
+   * Gets the UUID route.
+   *
+   * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
+   *   The entity type.
+   *
+   * @return \Symfony\Component\Routing\Route|null
+   *   The generated route, if available.
+   */
+  protected function getUuidRoute(EntityTypeInterface $entity_type) {
+    if ($entity_type->getKey('uuid') && $entity_type->hasViewBuilderClass() && $entity_type->hasLinkTemplate('uuid')) {
+      $entity_type_id = $entity_type->id();
+      $route = new Route($entity_type->getLinkTemplate('uuid'));
+      $route
+        ->addDefaults([
+          '_entity_view' => $entity_type_id . '.full',
+          '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title',
+        ])
+        ->setRequirement('_entity_access', $entity_type_id . '.view')
+        ->setOption('parameters', [
+          $entity_type_id => ['type' => 'entity:' . $entity_type_id],
+        ])
+        // Set requirement for UUID pattern.
+        ->setRequirement($entity_type_id, '^' . Uuid::VALID_PATTERN . '$');
+      return $route;
+    }
+  }
+
+  /**
    * Gets the edit-form route.
    *
    * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
diff --git a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
index 7159083..e5adf1f 100644
--- a/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
+++ b/core/lib/Drupal/Core/Menu/DefaultMenuLinkTreeManipulators.php
@@ -132,10 +132,36 @@ public function checkNodeAccess(array $tree) {
     $node_links = array();
     $this->collectNodeLinks($tree, $node_links);
     if ($node_links) {
-      $nids = array_keys($node_links);
+      // These could be serial node IDs or UUIDs.
+      $node_identifiers = array_keys($node_links);
+      $nids = array_filter($node_identifiers, function ($value) {
+        return is_int($value) || ctype_digit((string) $value);
+      });
+      $uuids = array_diff($node_identifiers, $nids);
 
-      $query = $this->queryFactory->get('node');
-      $query->condition('nid', $nids, 'IN');
+      // Create a query that will retrieve node IDs and UUIDs.
+      $query = $this->queryFactory->getAggregate('node');
+      if (!empty($nids) && !empty($uuids)) {
+        $query
+          ->groupBy('nid')
+          ->groupBy('uuid');
+        $group = $query
+          ->orConditionGroup()
+          ->condition('nid', $nids, 'IN')
+          ->condition('uuid', $uuids, 'IN');
+        $query->condition($group);
+        $result_keys = ['nid', 'uuid'];
+      }
+      elseif (!empty($nids)) {
+        $query->groupBy('nid');
+        $query->condition('nid', $nids, 'IN');
+        $result_keys = ['nid'];
+      }
+      else {
+        $query->groupBy('uuid');
+        $query->condition('uuid', $uuids, 'IN');
+        $result_keys = ['uuid'];
+      }
 
       // Allows admins to view all nodes, by both disabling node_access
       // query rewrite as well as not checking for the node status. The
@@ -150,10 +176,15 @@ public function checkNodeAccess(array $tree) {
         $query->condition('status', NODE_PUBLISHED);
       }
 
-      $nids = $query->execute();
-      foreach ($nids as $nid) {
-        foreach ($node_links[$nid] as $key => $link) {
-          $node_links[$nid][$key]->access = $access_result;
+      // Attach the access result to the menu tree for the use nodes the user is
+      // allowed to access.
+      foreach ($query->execute() as $result) {
+        foreach ($result_keys as $key) {
+          if (isset($node_links[$result[$key]])) {
+            foreach ($node_links[$result[$key]] as $node_link_key => $link) {
+              $node_links[$result[$key]][$node_link_key]->access = $access_result;
+            }
+          }
         }
       }
     }
@@ -174,7 +205,7 @@ public function checkNodeAccess(array $tree) {
    */
   protected function collectNodeLinks(array &$tree, array &$node_links) {
     foreach ($tree as $key => &$element) {
-      if ($element->link->getRouteName() == 'entity.node.canonical') {
+      if (in_array($element->link->getRouteName(), ['entity.node.canonical', 'entity.node.uuid'], TRUE)) {
         $nid = $element->link->getRouteParameters()['node'];
         $node_links[$nid][$key] = $element;
         // Deny access by default. checkNodeAccess() will re-add it.
diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
index 573cd49..7fd2db5 100644
--- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
+++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php
@@ -2,13 +2,14 @@
 
 namespace Drupal\Core\ParamConverter;
 
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\TypedData\TranslatableInterface;
 use Symfony\Component\Routing\Route;
 
 /**
- * Parameter converter for upcasting entity IDs to full objects.
+ * Parameter converter for upcasting entity IDs or UUIDs to full objects.
  *
  * This is useful in cases where the dynamic elements of the path can't be
  * auto-determined; for example, if your path refers to multiple of the same
@@ -57,18 +58,28 @@ public function __construct(EntityManagerInterface $entity_manager) {
 
   /**
    * {@inheritdoc}
+   *
+   * The value here can be either a serial entity ID, or the entity UUID.
    */
   public function convert($value, $definition, $name, array $defaults) {
     $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults);
+    $entity = NULL;
     if ($storage = $this->entityManager->getStorage($entity_type_id)) {
-      $entity = $storage->load($value);
+      // Load by UUID or ID depending on $value. Optimise skipping the UUID check and loading by UUID
+      if (!(is_int($value) || ctype_digit((string) $value)) && Uuid::isValid($value)) {
+        $entities = $storage->loadByProperties(['uuid' => $value]);
+        $entity = ($entities) ? reset($entities) : NULL;
+      }
+      if (!$entity) {
+        $entity = $storage->load($value);
+      }
       // If the entity type is translatable, ensure we return the proper
       // translation object for the current context.
       if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) {
         $entity = $this->entityManager->getTranslationFromContext($entity, NULL, array('operation' => 'entity_upcast'));
       }
-      return $entity;
     }
+    return $entity;
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index e7624d8..16fa897 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core;
 
 use Drupal\Component\Utility\UrlHelper;
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
@@ -325,7 +326,7 @@ public static function fromUri($uri, $options = []) {
    *
    * @param array $uri_parts
    *   Parts from an URI of the form entity:{entity_type}/{entity_id} as from
-   *   parse_url().
+   *   parse_url(). Note that {entity_id} can be either a UUID or the entity ID.
    * @param array $options
    *   An array of options, see \Drupal\Core\Url::fromUri() for details.
    * @param string $uri
@@ -340,10 +341,15 @@ public static function fromUri($uri, $options = []) {
   protected static function fromEntityUri(array $uri_parts, array $options, $uri) {
     list($entity_type_id, $entity_id) = explode('/', $uri_parts['path'], 2);
     if ($uri_parts['scheme'] != 'entity' || $entity_id === '') {
-      throw new \InvalidArgumentException("The entity URI '$uri' is invalid. You must specify the entity id in the URL. e.g., entity:node/1 for loading the canonical path to node entity with id 1.");
+      throw new \InvalidArgumentException("The entity URI '$uri' is invalid. You must specify the entity id in the URL. e.g., entity:node/1 or entity:node/{uuid} for loading the canonical path to node entity with id 1.");
+    }
+    $route_name = "entity.$entity_type_id.canonical";
+    if (Uuid::isValid($entity_id)) {
+      // UUID instead of entity ID.
+      $route_name = "entity.$entity_type_id.uuid";
     }
 
-    return new static("entity.$entity_type_id.canonical", [$entity_type_id => $entity_id], $options);
+    return new static($route_name, [$entity_type_id => $entity_id], $options);
   }
 
   /**
diff --git a/core/modules/aggregator/src/Entity/Feed.php b/core/modules/aggregator/src/Entity/Feed.php
index 834a55e..e997e68 100644
--- a/core/modules/aggregator/src/Entity/Feed.php
+++ b/core/modules/aggregator/src/Entity/Feed.php
@@ -31,6 +31,7 @@
  *   },
  *   links = {
  *     "canonical" = "/aggregator/sources/{aggregator_feed}",
+ *     "uuid" = "/aggregator/sources/{aggregator_feed}",
  *     "edit-form" = "/aggregator/sources/{aggregator_feed}/configure",
  *     "delete-form" = "/aggregator/sources/{aggregator_feed}/delete",
  *   },
diff --git a/core/modules/menu_ui/menu_ui.module b/core/modules/menu_ui/menu_ui.module
index 92254c2..5331cf2 100644
--- a/core/modules/menu_ui/menu_ui.module
+++ b/core/modules/menu_ui/menu_ui.module
@@ -201,8 +201,13 @@ function menu_ui_get_menu_link_defaults(NodeInterface $node) {
     // Give priority to the default menu
     $type_menus = $node_type->getThirdPartySetting('menu_ui', 'available_menus', array('main'));
     if (in_array($menu_name, $type_menus)) {
-      $query = \Drupal::entityQuery('menu_link_content')
-        ->condition('link.uri', 'node/' . $node->id())
+      $query = \Drupal::entityQuery('menu_link_content');
+      $group = $query->orConditionGroup()
+        ->condition('link.uri', 'entity:node/' . $node->id())
+        ->condition('link.uri', 'entity:node/' . $node->uuid())
+        ->condition('link.uri', 'internal:/node/' . $node->id())
+        ->condition('link.uri', 'internal:/node/' . $node->uuid());
+      $query->condition($group)
         ->condition('menu_name', $menu_name)
         ->sort('id', 'ASC')
         ->range(0, 1);
@@ -212,8 +217,13 @@ function menu_ui_get_menu_link_defaults(NodeInterface $node) {
     }
     // Check all allowed menus if a link does not exist in the default menu.
     if (!$id && !empty($type_menus)) {
-      $query = \Drupal::entityQuery('menu_link_content')
+      $query = \Drupal::entityQuery('menu_link_content');
+      $group = $query->orConditionGroup()
         ->condition('link.uri', 'entity:node/' . $node->id())
+        ->condition('link.uri', 'entity:node/' . $node->uuid())
+        ->condition('link.uri', 'internal:/node/' . $node->id())
+        ->condition('link.uri', 'internal:/node/' . $node->uuid());
+      $query->condition($group)
         ->condition('menu_name', array_values($type_menus), 'IN')
         ->sort('id', 'ASC')
         ->range(0, 1);
diff --git a/core/modules/menu_ui/src/Tests/MenuNodeTest.php b/core/modules/menu_ui/src/Tests/MenuNodeTest.php
index 3fc8423..07a8a43 100644
--- a/core/modules/menu_ui/src/Tests/MenuNodeTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuNodeTest.php
@@ -2,7 +2,8 @@
 
 namespace Drupal\menu_ui\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Core\Url;
+use Drupal\node\Entity\NodeType;
 use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\menu_link_content\Entity\MenuLinkContent;
 use Drupal\node\Entity\Node;
@@ -12,7 +13,7 @@
  *
  * @group menu_ui
  */
-class MenuNodeTest extends WebTestBase {
+class MenuNodeTest extends MenuWebTestBase {
 
   /**
    * An editor user.
@@ -338,4 +339,40 @@ function testMultilingualMenuNodeFormWidget() {
     $this->assertFieldById('edit-menu-title', $translated_node_title);
   }
 
+  /**
+   * Tests adding links to nodes using the /node/{uuid} format.
+   */
+  public function testNodeUuidLink() {
+    /* @var \Drupal\node\NodeTypeInterface $type */
+    $type = NodeType::load('page');
+    // Enable the main menu for this node type..
+    $menu_name = 'main';
+    $type->setThirdPartySetting('menu_ui', 'available_menus', [$menu_name]);
+    $type->save();
+
+    // Test links using node/{uuid}.
+    $node6 = $this->drupalCreateNode(array('type' => 'page'));
+    $uuid_link = $this->addMenuLink('', '/node/' . $node6->uuid(), $menu_name);
+    $this->verifyMenuLink($uuid_link, $node6);
+    $this->drupalGet($node6->url('edit-form'));
+    $this->assertFieldByName('menu[title]', $uuid_link->label());
+    $this->drupalPostForm(NULL, [], t('Save'));
+    \Drupal::entityManager()->getStorage('menu_link_content')->resetCache([$uuid_link->id()]);
+    /** @var \Drupal\menu_link_content\MenuLinkContentInterface $uuid_link */
+    $uuid_link = MenuLinkContent::load($uuid_link->id());
+    $this->assertEqual($uuid_link->getUrlObject(), Url::fromUri('internal:/node/' . $node6->uuid()));
+
+    // Test with entity:node/{uuid}.
+    $node7 = $this->drupalCreateNode(array('type' => 'page'));
+    $uuid_link = $this->addMenuLink('', 'entity:node/' . $node7->uuid(), $menu_name);
+    $this->verifyMenuLink($uuid_link, $node7);
+    $this->drupalGet($node7->url('edit-form'));
+    $this->assertFieldByName('menu[title]', $uuid_link->label());
+    $this->drupalPostForm(NULL, [], t('Save'));
+    \Drupal::entityManager()->getStorage('menu_link_content')->resetCache([$uuid_link->id()]);
+    /** @var \Drupal\menu_link_content\MenuLinkContentInterface $uuid_link */
+    $uuid_link = MenuLinkContent::load($uuid_link->id());
+    $this->assertEqual($uuid_link->getUrlObject(), Url::fromUri('entity:node/' . $node7->uuid()));
+  }
+
 }
diff --git a/core/modules/menu_ui/src/Tests/MenuTest.php b/core/modules/menu_ui/src/Tests/MenuTest.php
index e9464ab..a9151fa 100644
--- a/core/modules/menu_ui/src/Tests/MenuTest.php
+++ b/core/modules/menu_ui/src/Tests/MenuTest.php
@@ -69,7 +69,7 @@ protected function setUp() {
     $this->drupalCreateContentType(array('type' => 'article', 'name' => 'Article'));
 
     // Create users.
-    $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content'));
+    $this->adminUser = $this->drupalCreateUser(array('access administration pages', 'administer blocks', 'administer menu', 'create article content', 'edit any article content'));
     $this->authenticatedUser = $this->drupalCreateUser(array());
   }
 
@@ -221,6 +221,7 @@ function addCustomMenu() {
     // Enable the block.
     $block = $this->drupalPlaceBlock('system_menu_block:' . $menu_name);
     $this->blockPlacements[$menu_name] = $block->id();
+
     return Menu::load($menu_name);
   }
 
@@ -583,55 +584,6 @@ public function testBlockContextualLinks() {
   }
 
   /**
-   * Adds a menu link using the UI.
-   *
-   * @param string $parent
-   *   Optional parent menu link id.
-   * @param string $path
-   *   The path to enter on the form. Defaults to the front page.
-   * @param string $menu_name
-   *   Menu name. Defaults to 'tools'.
-   * @param bool $expanded
-   *   Whether or not this menu link is expanded. Setting this to TRUE should
-   *   test whether it works when we do the authenticatedUser tests. Defaults
-   *   to FALSE.
-   * @param string $weight
-   *   Menu weight. Defaults to 0.
-   *
-   * @return \Drupal\menu_link_content\Entity\MenuLinkContent
-   *   A menu link entity.
-   */
-  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);
-
-    $title = '!link_' . $this->randomMachineName(16);
-    $edit = array(
-      'link[0][uri]' => $path,
-      'title[0][value]' => $title,
-      'description[0][value]' => '',
-      'enabled[value]' => 1,
-      'expanded[value]' => $expanded,
-      'menu_parent' => $menu_name . ':' . $parent,
-      'weight[0][value]' => $weight,
-    );
-
-    // Add menu link.
-    $this->drupalPostForm(NULL, $edit, t('Save'));
-    $this->assertResponse(200);
-    $this->assertText('The menu link has been saved.');
-
-    $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $title));
-
-    $menu_link = reset($menu_links);
-    $this->assertTrue($menu_link, 'Menu link was found in database.');
-    $this->assertMenuLink($menu_link->getPluginId(), array('menu_name' => $menu_name, 'children' => array(), 'parent' => $parent));
-
-    return $menu_link;
-  }
-
-  /**
    * Attempts to add menu link with invalid path or no access permission.
    */
   function addInvalidMenuLink() {
@@ -688,45 +640,6 @@ function checkInvalidParentMenuLinks() {
   }
 
   /**
-   * Verifies a menu link using the UI.
-   *
-   * @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
-   *   Menu link.
-   * @param object $item_node
-   *   Menu link content node.
-   * @param \Drupal\menu_link_content\Entity\MenuLinkContent $parent
-   *   Parent menu link.
-   * @param object $parent_node
-   *   Parent menu link content node.
-   */
-  function verifyMenuLink(MenuLinkContent $item, $item_node, MenuLinkContent $parent = NULL, $parent_node = NULL) {
-    // View home page.
-    $this->drupalGet('');
-    $this->assertResponse(200);
-
-    // Verify parent menu link.
-    if (isset($parent)) {
-      // Verify menu link.
-      $title = $parent->getTitle();
-      $this->assertLink($title, 0, 'Parent menu link was displayed');
-
-      // Verify menu link link.
-      $this->clickLink($title);
-      $title = $parent_node->label();
-      $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Parent menu link link target was correct');
-    }
-
-    // Verify menu link.
-    $title = $item->getTitle();
-    $this->assertLink($title, 0, 'Menu link was displayed');
-
-    // Verify menu link link.
-    $this->clickLink($title);
-    $title = $item_node->label();
-    $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Menu link link target was correct');
-  }
-
-  /**
    * Changes the parent of a menu link using the UI.
    *
    * @param \Drupal\menu_link_content\MenuLinkContentInterface $item
diff --git a/core/modules/menu_ui/src/Tests/MenuWebTestBase.php b/core/modules/menu_ui/src/Tests/MenuWebTestBase.php
index c08fc14..e45068e 100644
--- a/core/modules/menu_ui/src/Tests/MenuWebTestBase.php
+++ b/core/modules/menu_ui/src/Tests/MenuWebTestBase.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\menu_ui\Tests;
 
+use Drupal\menu_link_content\Entity\MenuLinkContent;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -74,4 +75,97 @@ function assertMenuLink($menu_plugin_id, array $expected_item) {
     }
   }
 
+  /**
+   * Adds a menu link using the UI.
+   *
+   * @param string $parent
+   *   Optional parent menu link id.
+   * @param string $path
+   *   The path to enter on the form. Defaults to the front page.
+   * @param string $menu_name
+   *   Menu name. Defaults to 'tools'.
+   * @param bool $expanded
+   *   Whether or not this menu link is expanded. Setting this to TRUE should
+   *   test whether it works when we do the authenticatedUser tests. Defaults
+   *   to FALSE.
+   * @param string $weight
+   *   Menu weight. Defaults to 0.
+   *
+   * @return \Drupal\menu_link_content\Entity\MenuLinkContent
+   *   A menu link entity.
+   */
+  public 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);
+
+    $title = '!link_' . $this->randomMachineName(16);
+    $edit = array(
+      'link[0][uri]' => $path,
+      'title[0][value]' => $title,
+      'description[0][value]' => '',
+      'enabled[value]' => 1,
+      'expanded[value]' => $expanded,
+      'menu_parent' => $menu_name . ':' . $parent,
+      'weight[0][value]' => $weight,
+    );
+
+    // Add menu link.
+    $this->drupalPostForm(NULL, $edit, t('Save'));
+    $this->assertResponse(200);
+    $this->assertText('The menu link has been saved.');
+
+    $menu_links = entity_load_multiple_by_properties('menu_link_content', array('title' => $title));
+
+    $menu_link = reset($menu_links);
+    $this->assertTrue($menu_link, 'Menu link was found in database.');
+    $this->assertMenuLink($menu_link->getPluginId(), [
+      'menu_name' => $menu_name,
+      'children' => [],
+      'parent' => $parent,
+    ]);
+
+    return $menu_link;
+  }
+
+
+  /**
+   * Verifies a menu link using the UI.
+   *
+   * @param \Drupal\menu_link_content\Entity\MenuLinkContent $item
+   *   Menu link.
+   * @param object $item_node
+   *   Menu link content node.
+   * @param \Drupal\menu_link_content\Entity\MenuLinkContent $parent
+   *   Parent menu link.
+   * @param object $parent_node
+   *   Parent menu link content node.
+   */
+  public function verifyMenuLink(MenuLinkContent $item, $item_node, MenuLinkContent $parent = NULL, $parent_node = NULL) {
+    // View home page.
+    $this->drupalGet('');
+    $this->assertResponse(200);
+
+    // Verify parent menu link.
+    if (isset($parent)) {
+      // Verify menu link.
+      $title = $parent->getTitle();
+      $this->assertLink($title, 0, 'Parent menu link was displayed');
+
+      // Verify menu link link.
+      $this->clickLink($title);
+      $title = $parent_node->label();
+      $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Parent menu link link target was correct');
+    }
+
+    // Verify menu link.
+    $title = $item->getTitle();
+    $this->assertLink($title, 0, 'Menu link was displayed');
+
+    // Verify menu link link.
+    $this->clickLink($title);
+    $title = $item_node->label();
+    $this->assertTitle(t("@title | Drupal", array('@title' => $title)), 'Menu link link target was correct');
+  }
+
 }
diff --git a/core/modules/node/src/Entity/Node.php b/core/modules/node/src/Entity/Node.php
index 68afc83..4b138f2 100644
--- a/core/modules/node/src/Entity/Node.php
+++ b/core/modules/node/src/Entity/Node.php
@@ -66,6 +66,7 @@
  *   permission_granularity = "bundle",
  *   links = {
  *     "canonical" = "/node/{node}",
+ *     "uuid" = "/node/{node}",
  *     "delete-form" = "/node/{node}/delete",
  *     "edit-form" = "/node/{node}/edit",
  *     "version-history" = "/node/{node}/revisions",
diff --git a/core/modules/node/src/Entity/NodeRouteProvider.php b/core/modules/node/src/Entity/NodeRouteProvider.php
index 0803f1e..b46b09d 100644
--- a/core/modules/node/src/Entity/NodeRouteProvider.php
+++ b/core/modules/node/src/Entity/NodeRouteProvider.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\node\Entity;
 
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
 use Symfony\Component\Routing\Route;
@@ -17,6 +18,17 @@ class NodeRouteProvider implements EntityRouteProviderInterface {
    */
   public function getRoutes( EntityTypeInterface $entity_type) {
     $route_collection = new RouteCollection();
+
+    $route = (new Route("/node/{node}"))
+      ->addDefaults([
+        '_controller' => '\Drupal\node\Controller\NodeViewController::view',
+        '_title_callback' => '\Drupal\node\Controller\NodeViewController::title',
+      ])
+      // Set requirement for UUID pattern.
+      ->setRequirement('node', '^' . Uuid::VALID_PATTERN . '$')
+      ->setRequirement('_entity_access', 'node.view');
+    $route_collection->add('entity.node.uuid', $route);
+
     $route = (new Route('/node/{node}'))
       ->addDefaults([
         '_controller' => '\Drupal\node\Controller\NodeViewController::view',
diff --git a/core/modules/node/src/Tests/NodeAccessMenuLinkTest.php b/core/modules/node/src/Tests/NodeAccessMenuLinkTest.php
index c1bc0eb..d10208b 100644
--- a/core/modules/node/src/Tests/NodeAccessMenuLinkTest.php
+++ b/core/modules/node/src/Tests/NodeAccessMenuLinkTest.php
@@ -44,29 +44,57 @@ protected function setUp() {
    */
   function testNodeAccessMenuLink() {
 
-    $menu_link_title = $this->randomString();
+    $nid_menu_link_title = $this->randomString();
 
     $this->drupalLogin($this->contentAdminUser);
+    $node_title = $this->randomString();
     $edit = [
-      'title[0][value]' => $this->randomString(),
+      'title[0][value]' => $node_title,
       'body[0][value]' => $this->randomString(),
       'menu[enabled]' => 1,
-      'menu[title]' => $menu_link_title,
+      'menu[title]' => $nid_menu_link_title,
     ];
     $this->drupalPostForm('node/add/page', $edit, t('Save'));
-    $this->assertLink($menu_link_title);
+
+    // Create another node to link by UUID.
+    $node = $this->createNode();
+    $uuid_menu_link_title = '!link_' . $this->randomMachineName(16);
+    $edit = array(
+      'link[0][uri]' => 'entity:node/' . $node->uuid(),
+      'title[0][value]' => $uuid_menu_link_title,
+      'description[0][value]' => '',
+      'enabled[value]' => 1,
+      'menu_parent' => 'main:',
+    );
+    // Add menu link.
+    $this->drupalPostForm('admin/structure/menu/manage/main/add', $edit, t('Save'));
+
+    // Ensure you can add a child link to both node menu links.
+    // @see \Drupal\Core\Menu\DefaultMenuLinkTreeManipulators::checkNodeAccess()
+    $this->drupalGet('admin/structure/menu/manage/main/add');
+    $elements = $this->xpath('//select["edit-menu-parent"]//option[contains(text(), :title)]', [':title' => $uuid_menu_link_title]);
+    $this->assertTrue(is_array($elements) && isset($elements[0]), 'Node menu item using UUID can be a parent');
+    $elements = $this->xpath('//select["edit-menu-parent"]//option[contains(text(), :title)]', [':title' => $nid_menu_link_title]);
+    $this->assertTrue(is_array($elements) && isset($elements[0]), 'Node menu item using node ID can be a parent');
+
+    // Ensure the menu links appear for a user with the correct permissions.
+    $this->drupalGet('');
+    $this->assertLink($nid_menu_link_title);
+    $this->assertLink($uuid_menu_link_title);
 
     // Ensure anonymous users without "access content" permission do not see
     // this menu link.
     $this->drupalLogout();
     $this->drupalGet('');
-    $this->assertNoLink($menu_link_title);
+    $this->assertNoLink($nid_menu_link_title);
+    $this->assertNoLink($uuid_menu_link_title);
 
     // Ensure anonymous users with "access content" permission see this menu
     // link.
     $this->config('user.role.' . RoleInterface::ANONYMOUS_ID)->set('permissions', array('access content'))->save();
     $this->drupalGet('');
-    $this->assertLink($menu_link_title);
+    $this->assertLink($nid_menu_link_title);
+    $this->assertLink($uuid_menu_link_title);
   }
 
 }
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 6dc2663..56a36c9 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -1794,3 +1794,19 @@ function system_update_8203() {
     ->set('logging', 1)
     ->save(TRUE);
 }
+
+/**
+ * @addtogroup updates-8.3.0
+ * @{
+ */
+
+/**
+ * The simple presence of this update function clears cached entity definitions.
+ */
+function system_update_8300() {
+  // Many core entity-types now have a UUID link template and route.
+}
+
+/**
+ * @} End of "addtogroup updates-8.2.0".
+ */
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
index a603f53..38ac4ca 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTest.php
@@ -42,6 +42,7 @@
  *   },
  *   links = {
  *     "canonical" = "/entity_test/{entity_test}",
+ *     "uuid" = "/entity_test/{entity_test}",
  *     "add-form" = "/entity_test/add",
  *     "edit-form" = "/entity_test/manage/{entity_test}/edit",
  *     "delete-form" = "/entity_test/delete/entity_test/{entity_test}",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
index f7fe8e6..629a6b5 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestBaseFieldDisplay.php
@@ -33,6 +33,7 @@
  *   },
  *   links = {
  *     "canonical" = "/entity_test_base_field_display/{entity_test_base_field_display}/edit",
+ *     "uuid" = "/entity_test_base_field_display/{entity_test_base_field_display}/edit",
  *     "add-form" = "/entity_test_base_field_display/add",
  *     "edit-form" = "/entity_test_base_field_display/manage/{entity_test_base_field_display}",
  *     "delete-form" = "/entity_test/delete/entity_test_base_field_display/{entity_test_base_field_display}/edit",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
index cbe7ba4..3a11384 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php
@@ -36,6 +36,7 @@
  *     "add-page" = "/entity_test_mul/add",
  *     "add-form" = "/entity_test_mul/add/{type}",
  *     "canonical" = "/entity_test_mul/manage/{entity_test_mul}",
+ *     "uuid" = "/entity_test_mul/manage/{entity_test_mul}",
  *     "edit-form" = "/entity_test_mul/manage/{entity_test_mul}/edit",
  *     "delete-form" = "/entity_test/delete/entity_test_mul/{entity_test_mul}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php
index 5fbc408..f7426c2 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulChanged.php
@@ -39,6 +39,7 @@
  *   links = {
  *     "add-form" = "/entity_test_mul_changed/add",
  *     "canonical" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}",
+ *     "uuid" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}",
  *     "edit-form" = "/entity_test_mul_changed/manage/{entity_test_mul_changed}/edit",
  *     "delete-form" = "/entity_test/delete/entity_test_mul_changed/{entity_test_mul_changed}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php
index 94948a3..adc4357 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulLangcodeKey.php
@@ -36,6 +36,7 @@
  *   links = {
  *     "add-form" = "/entity_test_mul_langcode_key/add",
  *     "canonical" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}",
+ *     "uuid" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}",
  *     "edit-form" = "/entity_test_mul_langcode_key/manage/{entity_test_mul_langcode_key}/edit",
  *     "delete-form" = "/entity_test/delete/entity_test_mul_langcode_key/{entity_test_mul_langcode_key}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
index f01675f..e059cfa 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php
@@ -39,6 +39,7 @@
  *   links = {
  *     "add-form" = "/entity_test_mulrev/add",
  *     "canonical" = "/entity_test_mulrev/manage/{entity_test_mulrev}",
+ *     "uuid" = "/entity_test_mulrev/manage/{entity_test_mulrev}",
  *     "delete-form" = "/entity_test/delete/entity_test_mulrev/{entity_test_mulrev}",
  *     "edit-form" = "/entity_test_mulrev/manage/{entity_test_mulrev}/edit",
  *     "revision" = "/entity_test_mulrev/{entity_test_mulrev}/revision/{entity_test_mulrev_revision}/view",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php
index 3913075..500b1e3 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRevChanged.php
@@ -40,6 +40,7 @@
  *   links = {
  *     "add-form" = "/entity_test_mulrev_changed/add",
  *     "canonical" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}",
+ *     "uuid" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}",
  *     "delete-form" = "/entity_test/delete/entity_test_mulrev_changed/{entity_test_mulrev_changed}",
  *     "edit-form" = "/entity_test_mulrev_changed/manage/{entity_test_mulrev_changed}/edit",
  *     "revision" = "/entity_test_mulrev_changed/{entity_test_mulrev_changed}/revision/{entity_test_mulrev_changed_revision}/view",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
index bcbb17b..6bd9abb 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestRev.php
@@ -40,6 +40,7 @@
  *   links = {
  *     "add-form" = "/entity_test_rev/add",
  *     "canonical" = "/entity_test_rev/manage/{entity_test_rev}",
+ *     "uuid" = "/entity_test_rev/manage/{entity_test_rev}",
  *     "delete-form" = "/entity_test/delete/entity_test_rev/{entity_test_rev}",
  *     "edit-form" = "/entity_test_rev/manage/{entity_test_rev}/edit",
  *     "revision" = "/entity_test_rev/{entity_test_rev}/revision/{entity_test_rev_revision}/view",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php
index 342cbb9..723aa55 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestStringId.php
@@ -30,6 +30,7 @@
  *   },
  *   links = {
  *     "canonical" = "/entity_test_string_id/manage/{entity_test_string_id}",
+ *     "uuid" = "/entity_test_string_id/manage/{entity_test_string_id}",
  *     "add-form" = "/entity_test_string_id/add",
  *     "edit-form" = "/entity_test_string_id/manage/{entity_test_string_id}",
  *   },
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php
index c668bd3..7ef8e3a 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithBundle.php
@@ -37,6 +37,7 @@
  *   bundle_entity_type = "entity_test_bundle",
  *   links = {
  *     "canonical" = "/entity_test_with_bundle/{entity_test_with_bundle}",
+ *     "uuid" = "/entity_test_with_bundle/{entity_test_with_bundle}",
  *     "add-page" = "/entity_test_with_bundle/add",
  *     "add-form" = "/entity_test_with_bundle/add/{entity_test_bundle}",
  *     "edit-form" = "/entity_test_with_bundle/{entity_test_with_bundle}/edit",
diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php
index 4f4f4f1..465e663 100644
--- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php
+++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestWithRevisionLog.php
@@ -37,6 +37,7 @@
  *   },
  *   links = {
  *     "canonical" = "/entity_test_revlog/manage/{entity_test_revlog}",
+ *     "uuid" = "/entity_test_revlog/manage/{entity_test_revlog}",
  *     "delete-form" = "/entity_test/delete/entity_test_revlog/{entity_test_revlog}",
  *     "edit-form" = "/entity_test_revlog/manage/{entity_test_revlog}/edit",
  *     "revision" = "/entity_test_revlog/{entity_test_revlog}/revision/{entity_test_revlog_revision}/view",
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index c8ef06e..35c583a 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -44,6 +44,7 @@
  *   common_reference_target = TRUE,
  *   links = {
  *     "canonical" = "/taxonomy/term/{taxonomy_term}",
+ *     "uuid" = "/taxonomy/term/{taxonomy_term}",
  *     "delete-form" = "/taxonomy/term/{taxonomy_term}/delete",
  *     "edit-form" = "/taxonomy/term/{taxonomy_term}/edit",
  *   },
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index 8a3bd1a..4957fa9 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -76,6 +76,16 @@ entity.taxonomy_vocabulary.overview_form:
   requirements:
     _entity_access: 'taxonomy_vocabulary.view'
 
+entity.taxonomy_term.uuid:
+  path: '/taxonomy/term/{taxonomy_term}'
+  defaults:
+    _entity_view: 'taxonomy_term.full'
+    _title: 'Taxonomy term'
+    _title_callback: '\Drupal\taxonomy\Controller\TaxonomyController::termTitle'
+  requirements:
+    _entity_access: 'taxonomy_term.view'
+    taxonomy_term: '[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}'
+
 entity.taxonomy_term.canonical:
   path: '/taxonomy/term/{taxonomy_term}'
   defaults:
diff --git a/core/modules/user/src/Entity/User.php b/core/modules/user/src/Entity/User.php
index 062af44..b482637 100644
--- a/core/modules/user/src/Entity/User.php
+++ b/core/modules/user/src/Entity/User.php
@@ -48,6 +48,7 @@
  *   },
  *   links = {
  *     "canonical" = "/user/{user}",
+ *     "uuid" = "/user/{user}",
  *     "edit-form" = "/user/{user}/edit",
  *     "cancel-form" = "/user/{user}/cancel",
  *     "collection" = "/admin/people",
diff --git a/core/modules/user/src/Entity/UserRouteProvider.php b/core/modules/user/src/Entity/UserRouteProvider.php
index d1e9671..14543bb 100644
--- a/core/modules/user/src/Entity/UserRouteProvider.php
+++ b/core/modules/user/src/Entity/UserRouteProvider.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\user\Entity;
 
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Entity\Routing\EntityRouteProviderInterface;
 use Symfony\Component\Routing\Route;
@@ -17,6 +18,17 @@ class UserRouteProvider implements EntityRouteProviderInterface {
    */
   public function getRoutes(EntityTypeInterface $entity_type) {
     $route_collection = new RouteCollection();
+
+    $route = (new Route("/user/{user}"))
+      ->addDefaults([
+        '_entity_view' => 'user.full',
+        '_title_callback' => 'Drupal\user\Controller\UserController::userTitle',
+      ])
+      // Set requirement for UUID pattern.
+      ->setRequirement('user', '^' . Uuid::VALID_PATTERN . '$')
+      ->setRequirement('_entity_access', 'user.view');
+    $route_collection->add('entity.user.uuid', $route);
+
     $route = (new Route('/user/{user}'))
       ->setDefaults([
         '_entity_view' => 'user.full',
diff --git a/core/modules/views/src/Plugin/views/argument_validator/Entity.php b/core/modules/views/src/Plugin/views/argument_validator/Entity.php
index 53e29d7..c3df9ca 100644
--- a/core/modules/views/src/Plugin/views/argument_validator/Entity.php
+++ b/core/modules/views/src/Plugin/views/argument_validator/Entity.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\views\Plugin\views\argument_validator;
 
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
@@ -168,8 +169,27 @@ public function validateArgument($argument) {
     else {
       return FALSE;
     }
+    $uuids = array_filter($ids, function ($value) {
+      return Uuid::isValid($value);
+    });
+    $ids = array_diff($ids, $uuids);
+
+    $storage = $this->entityManager->getStorage($entity_type);
+    if ($uuids) {
+      $entities = $storage->loadByProperties(['uuid' => $uuids]);
+      if (count($entities) !== count($uuids)) {
+        // Missing one or more UUIDs, break and return false.
+        return FALSE;
+      }
+
+      foreach ($entities as $entity) {
+        if (!$this->validateEntity($entity)) {
+          return FALSE;
+        }
+      }
+    }
+    $entities = $storage->loadMultiple($ids);
 
-    $entities = $this->entityManager->getStorage($entity_type)->loadMultiple($ids);
     // Validate each id => entity. If any fails break out and return false.
     foreach ($ids as $id) {
       // There is no entity for this ID.
diff --git a/core/modules/views/tests/src/Unit/Plugin/argument_validator/EntityTest.php b/core/modules/views/tests/src/Unit/Plugin/argument_validator/EntityTest.php
index f9ca7d8..3afebf3 100644
--- a/core/modules/views/tests/src/Unit/Plugin/argument_validator/EntityTest.php
+++ b/core/modules/views/tests/src/Unit/Plugin/argument_validator/EntityTest.php
@@ -88,6 +88,13 @@ protected function setUp() {
       ->method('loadMultiple')
       ->will($this->returnValueMap($value_map));
 
+    $uuid_value_map = [
+      [['uuid' => ['fb0920d9-7a0d-42c5-8d11-b31d9ebfae6b']], [1 => $mock_entity]],
+    ];
+    $storage->expects($this->any())
+      ->method('loadByProperties')
+      ->will($this->returnValueMap($uuid_value_map));
+
     $this->entityManager->expects($this->any())
       ->method('getStorage')
       ->with('entity_test')
@@ -122,7 +129,9 @@ public function testValidateArgumentNoAccess() {
     $this->assertFalse($this->argumentValidator->validateArgument(''));
 
     $this->assertTrue($this->argumentValidator->validateArgument(1));
-    $this->assertTrue($this->argumentValidator->validateArgument(2));
+    $this->assertTrue($this->argumentValidator->validateArgument(1));
+    $this->assertTrue($this->argumentValidator->validateArgument('fb0920d9-7a0d-42c5-8d11-b31d9ebfae6b'));
+    $this->assertFalse($this->argumentValidator->validateArgument('298109c5-c931-4367-af2b-356008905ed1'));
     $this->assertFalse($this->argumentValidator->validateArgument('1,2'));
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
index a6675ec..0b128aa 100644
--- a/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/Routing/DefaultHtmlRouteProviderTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Entity\Routing;
 
+use Drupal\Component\Uuid\Uuid;
 use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
 use Drupal\Core\Entity\EntityFieldManagerInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
@@ -299,6 +300,59 @@ public function providerTestGetCollectionRoute() {
   }
 
   /**
+   * @covers ::getUuidRoute
+   * @dataProvider providerTestGetUuidRoute
+   */
+  public function testGetUuidRoute(EntityTypeInterface $entity_type, Route $expected = NULL) {
+    $route = $this->routeProvider->getUuidRoute($entity_type);
+    $this->assertEquals($expected, $route);
+  }
+
+  public function providerTestGetUuidRoute() {
+    $data = [];
+
+    $entity_type1 = $this->getEntityType();
+    $entity_type1->getKey('uuid')->willReturn(FALSE);
+    $data['no_canonical_link_template'] = [$entity_type1->reveal()];
+
+    $entity_type2 = $this->getEntityType();;
+    $entity_type2->getKey('uuid')->willReturn(TRUE);
+    $entity_type2->hasViewBuilderClass()->willReturn(FALSE);
+    $data['no_view_builder'] = [$entity_type2->reveal()];
+
+    $entity_type3 = $this->getEntityType($entity_type2);;
+    $entity_type3->hasViewBuilderClass()->willReturn(TRUE);
+    $entity_type3->hasLinkTemplate('uuid')->willReturn(FALSE);
+    $data['no_uuid_link_template'] = [$entity_type2->reveal()];
+
+    $entity_type4 = $this->getEntityType($entity_type2);
+    $entity_type4->hasViewBuilderClass()->willReturn(TRUE);
+    $entity_type4->id()->willReturn('the_entity_type_id');
+    $entity_type4->getKey('uuid')->willReturn(TRUE);
+    $entity_type4->hasLinkTemplate('uuid')->willReturn(TRUE);
+    $entity_type4->getLinkTemplate('uuid')->willReturn('/the_entity_type_id/{the_entity_type_id}');
+    $entity_type4->isSubclassOf(FieldableEntityInterface::class)->willReturn(FALSE);
+    $route = (new Route('/the_entity_type_id/{the_entity_type_id}'))
+      ->setDefaults([
+        '_entity_view' => 'the_entity_type_id.full',
+        '_title_callback' => '\Drupal\Core\Entity\Controller\EntityController::title',
+      ])
+      ->setRequirements([
+        '_entity_access' => 'the_entity_type_id.view',
+        'the_entity_type_id' => '^' . Uuid::VALID_PATTERN . '$',
+      ])
+      ->setOptions([
+        'parameters' => [
+          'the_entity_type_id' => [
+            'type' => 'entity:the_entity_type_id',
+          ],
+        ],
+      ]);
+    $data['has_uuid_route'] = [$entity_type4->reveal(), $route];
+    return $data;
+  }
+
+  /**
    * @covers ::getEntityTypeIdKeyType
    */
   public function testGetEntityTypeIdKeyType() {
@@ -362,5 +416,8 @@ public function getCanonicalRoute(EntityTypeInterface $entity_type) {
   public function getCollectionRoute(EntityTypeInterface $entity_type) {
     return parent::getCollectionRoute($entity_type);
   }
+  public function getUuidRoute(EntityTypeInterface $entity_type) {
+    return parent::getUuidRoute($entity_type);
+  }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
index 2000fe6..156d490 100644
--- a/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/DefaultMenuLinkTreeManipulatorsTest.php
@@ -5,6 +5,8 @@
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Cache\Context\CacheContextsManager;
 use Drupal\Core\DependencyInjection\Container;
+use Drupal\Core\Entity\Query\ConditionInterface;
+use Drupal\Core\Entity\Query\QueryAggregateInterface;
 use Drupal\Core\Menu\DefaultMenuLinkTreeManipulators;
 use Drupal\Core\Menu\MenuLinkTreeElement;
 use Drupal\Tests\UnitTestCase;
@@ -73,7 +75,6 @@ protected function setUp() {
     $this->queryFactory = $this->getMockBuilder('Drupal\Core\Entity\Query\QueryFactory')
       ->disableOriginalConstructor()
       ->getMock();
-
     $this->defaultMenuTreeManipulators = new DefaultMenuLinkTreeManipulators($this->accessManager, $this->currentUser, $this->queryFactory);
 
     $cache_contexts_manager = $this->prophesize(CacheContextsManager::class);
@@ -263,44 +264,70 @@ public function testFlatten() {
   }
 
   /**
-   * Tests the optimized node access checking.
+   * Tests the optimized node access checking with both node IDs or UUIDs.
    *
    * @covers ::checkNodeAccess
    * @covers ::collectNodeLinks
    * @covers ::checkAccess
    */
-  public function testCheckNodeAccess() {
+  public function testCheckNodeAccessUuidAndNid() {
     $links = array(
       1 => MenuLinkMock::create(array('id' => 'node.1', 'route_name' => 'entity.node.canonical', 'title' => 'foo', 'parent' => '', 'route_parameters' => array('node' => 1))),
       2 => MenuLinkMock::create(array('id' => 'node.2', 'route_name' => 'entity.node.canonical', 'title' => 'bar', 'parent' => '', 'route_parameters' => array('node' => 2))),
       3 => MenuLinkMock::create(array('id' => 'node.3', 'route_name' => 'entity.node.canonical', 'title' => 'baz', 'parent' => 'node.2', 'route_parameters' => array('node' => 3))),
-      4 => MenuLinkMock::create(array('id' => 'node.4', 'route_name' => 'entity.node.canonical', 'title' => 'qux', 'parent' => 'node.3', 'route_parameters' => array('node' => 4))),
-      5 => MenuLinkMock::create(array('id' => 'test.1', 'route_name' => 'test_route', 'title' => 'qux', 'parent' => '')),
-      6 => MenuLinkMock::create(array('id' => 'test.2', 'route_name' => 'test_route', 'title' => 'qux', 'parent' => 'test.1')),
+      4 => MenuLinkMock::create(array('id' => 'node.4', 'route_name' => 'entity.node.uuid', 'title' => 'qux', 'parent' => 'node.3', 'route_parameters' => array('node' => 4))),
+      5 => MenuLinkMock::create(array('id' => 'node.5', 'route_name' => 'entity.node.uuid', 'title' => 'qux', 'parent' => 'node.3', 'route_parameters' => array('node' => '7910d537-04be-450a-8200-b53f49ea6b30'))),
+      6 => MenuLinkMock::create(array('id' => 'test.1', 'route_name' => 'test_route', 'title' => 'qux', 'parent' => '')),
+      7 => MenuLinkMock::create(array('id' => 'test.2', 'route_name' => 'test_route', 'title' => 'qux', 'parent' => 'test.1')),
     );
     $tree = array();
     $tree[1] = new MenuLinkTreeElement($links[1], FALSE, 1, FALSE, array());
     $tree[2] = new MenuLinkTreeElement($links[2], TRUE, 1, FALSE, array(
       3 => new MenuLinkTreeElement($links[3], TRUE, 2, FALSE, array(
         4 => new MenuLinkTreeElement($links[4], FALSE, 3, FALSE, array()),
+        5 => new MenuLinkTreeElement($links[5], FALSE, 3, FALSE, array()),
       )),
     ));
-    $tree[5] = new MenuLinkTreeElement($links[5], TRUE, 1, FALSE, array(
-      6 => new MenuLinkTreeElement($links[6], FALSE, 2, FALSE, array()),
+    $tree[6] = new MenuLinkTreeElement($links[6], TRUE, 1, FALSE, array(
+      7 => new MenuLinkTreeElement($links[7], FALSE, 2, FALSE, array()),
     ));
 
-    $query = $this->getMock('Drupal\Core\Entity\Query\QueryInterface');
+    $query = $this->getMock(QueryAggregateInterface::class);
+    $condition = $this->getMock(ConditionInterface::class);
     $query->expects($this->at(0))
-      ->method('condition')
-      ->with('nid', array(1, 2, 3, 4));
+      ->method('groupBy')
+      ->with('nid')
+      ->willReturn($query);
     $query->expects($this->at(1))
+      ->method('groupBy')
+      ->with('uuid')
+      ->willReturn($query);
+    $query->expects($this->once())
+      ->method('orConditionGroup')
+      ->willReturn($condition);
+    $condition->expects($this->at(0))
+      ->method('condition')
+      ->with('nid', [1, 2, 3, 4])
+      ->willReturn($condition);
+    $condition->expects($this->at(1))
+      ->method('condition')
+      ->with('uuid', [4 => '7910d537-04be-450a-8200-b53f49ea6b30'])
+      ->willReturn($condition);
+    $query->expects($this->at(3))
+      ->method('condition')
+      ->with($condition);
+    $query->expects($this->at(4))
       ->method('condition')
       ->with('status', NODE_PUBLISHED);
     $query->expects($this->once())
       ->method('execute')
-      ->willReturn(array(1, 2, 4));
+      ->willReturn([
+        ['nid' => 1, 'uuid' => '9d7316d5-2614-4679-bc00-ec677f248e4e'],
+        ['nid' => 2, 'uuid' => '2f347698-e682-442b-9d00-f756bf6bec6d'],
+        ['nid' => 4, 'uuid' => '7910d537-04be-450a-8200-b53f49ea6b30'],
+      ]);
     $this->queryFactory->expects($this->once())
-      ->method('get')
+      ->method('getAggregate')
       ->with('node')
       ->willReturn($query);
 
@@ -313,8 +340,8 @@ public function testCheckNodeAccess() {
     $this->assertEquals(AccessResult::neutral(), $tree[2]->subtree[3]->access);
     $this->assertEquals($node_access_result, $tree[2]->subtree[3]->subtree[4]->access);
     // Ensure that other routes than entity.node.canonical are set as well.
-    $this->assertNull($tree[5]->access);
-    $this->assertNull($tree[5]->subtree[6]->access);
+    $this->assertNull($tree[6]->access);
+    $this->assertNull($tree[6]->subtree[7]->access);
 
     // On top of the node access checking now run the ordinary route based
     // access checkers.
@@ -333,8 +360,69 @@ public function testCheckNodeAccess() {
     $this->assertEquals($node_access_result, $tree[1]->access);
     $this->assertEquals($node_access_result, $tree[2]->access);
     $this->assertEquals(AccessResult::neutral(), $tree[2]->subtree[3]->access);
-    $this->assertEquals(AccessResult::allowed()->cachePerPermissions(), $tree[5]->access);
-    $this->assertEquals(AccessResult::neutral()->cachePerPermissions(), $tree[5]->subtree[6]->access);
+    $this->assertEquals(AccessResult::allowed()->cachePerPermissions(), $tree[6]->access);
+    $this->assertEquals(AccessResult::neutral()->cachePerPermissions(), $tree[6]->subtree[7]->access);
+  }
+
+  /**
+   * Tests the optimized node access checking with only node IDs or UUIDs.
+   *
+   * @covers ::checkNodeAccess
+   * @covers ::collectNodeLinks
+   * @covers ::checkAccess
+   *
+   * @dataProvider providerCheckNodeAccessNidOrUuidOnly
+   */
+  public function testCheckNodeAccessNidOrUuidOnly($key, array $values) {
+    $links = [
+      1 => MenuLinkMock::create(['id' => 'node.1', 'route_name' => 'entity.node.canonical', 'title' => 'foo', 'parent' => '', 'route_parameters' => ['node' => $values[0]]]),
+      2 => MenuLinkMock::create(['id' => 'node.2', 'route_name' => 'entity.node.canonical', 'title' => 'bar', 'parent' => '', 'route_parameters' => ['node' => $values[1]]]),
+      3 => MenuLinkMock::create(['id' => 'node.3', 'route_name' => 'entity.node.canonical', 'title' => 'bar', 'parent' => '', 'route_parameters' => ['node' => $values[2]]]),
+    ];
+    $tree = [];
+    $tree[1] = new MenuLinkTreeElement($links[1], FALSE, 1, FALSE, []);
+    $tree[2] = new MenuLinkTreeElement($links[2], TRUE, 1, FALSE, []);
+    $tree[3] = new MenuLinkTreeElement($links[3], TRUE, 1, FALSE, []);
+
+    $query = $this->getMock(QueryAggregateInterface::class);
+    $query->expects($this->at(0))
+      ->method('groupBy')
+      ->with($key)
+      ->willReturn($query);
+    $query->expects($this->at(1))
+      ->method('condition')
+      ->with($key, $values, 'IN');
+    $query->expects($this->at(2))
+      ->method('condition')
+      ->with('status', NODE_PUBLISHED);
+    $query->expects($this->once())
+      ->method('execute')
+      ->willReturn([
+        [$key => $values[0]],
+        [$key => $values[1]],
+      ]);
+    $this->queryFactory->expects($this->once())
+      ->method('getAggregate')
+      ->with('node')
+      ->willReturn($query);
+
+    $node_access_result = AccessResult::allowed()->cachePerPermissions()->addCacheContexts(['user.node_grants:view']);
+
+    $tree = $this->defaultMenuTreeManipulators->checkNodeAccess($tree);
+    $this->assertEquals($node_access_result, $tree[1]->access);
+    $this->assertEquals($node_access_result, $tree[2]->access);
+    // Ensure that access denied is set.
+    $this->assertEquals(AccessResult::neutral(), $tree[3]->access);
+  }
+
+  /**
+   * Data provider for self::testCheckNodeAccessNidOrUuidOnly().
+   */
+  public function providerCheckNodeAccessNidOrUuidOnly() {
+    return [
+      ['nid', [1, 2, 3]],
+      ['uuid', ['9d7316d5-2614-4679-bc00-ec677f248e4e', '2f347698-e682-442b-9d00-f756bf6bec6d', '7910d537-04be-450a-8200-b53f49ea6b30']],
+    ];
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php b/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php
index ebafab0..7b6fcc8 100644
--- a/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php
+++ b/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php
@@ -89,6 +89,12 @@ public function testConvert($value, array $definition, array $defaults, $expecte
         ['valid_id', (object) ['id' => 'valid_id']],
         ['invalid_id', NULL],
       ]);
+    $entity_storage->expects($this->any())
+      ->method('loadByProperties')
+      ->willReturnMap([
+        [['uuid' => 'invalid_id'], NULL],
+        [['uuid' => $value], [(object) ['uuid' => $value, 'id' => 'valid_id']]],
+      ]);
 
     $this->assertEquals($expected_result, $this->entityConverter->convert($value, $definition, 'foo', $defaults));
   }
@@ -104,6 +110,8 @@ public function providerTestConvert() {
     $data[] = ['invalid_id', ['type' => 'entity:entity_test'], ['foo' => 'invalid_id'], NULL];
     // Entity type placeholder.
     $data[] = ['valid_id', ['type' => 'entity:{entity_type}'], ['foo' => 'valid_id', 'entity_type' => 'entity_test'], (object) ['id' => 'valid_id']];
+    // UUID.
+    $data[] = ['1c5217f4-553c-40d8-8389-a3cc3529d79c', ['type' => 'entity:entity_test'], ['foo' => '1c5217f4-553c-40d8-8389-a3cc3529d79c'], (object) ['uuid' => '1c5217f4-553c-40d8-8389-a3cc3529d79c', 'id' => 'valid_id']];
 
     return $data;
   }
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index 5324c26..f8f6c2e 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -582,6 +582,14 @@ public function providerTestEntityUris() {
         ['page' => '1', 'foo' => 'yes', 'focus' => 'no'],
         'top',
       ],
+      [
+        'entity:test_entity/d44a0040-2844-4cca-b0b5-20c6c96c4d8c',
+        ['fragment' => ''],
+        'entity.test_entity.uuid',
+        ['test_entity' => 'd44a0040-2844-4cca-b0b5-20c6c96c4d8c'],
+        NULL,
+        NULL,
+      ],
 
     ];
   }
