diff --git a/core/core.services.yml b/core/core.services.yml
index 7abab67..7af5d4b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -494,7 +494,7 @@ services:
     arguments: ['@request_stack', '@config.factory' ]
   link_generator:
     class: Drupal\Core\Utility\LinkGenerator
-    arguments: ['@url_generator', '@module_handler']
+    arguments: ['@url_generator', '@module_handler', '@path_processor_manager', '@request_stack']
   router:
     class: Drupal\Core\Routing\AccessAwareRouter
     arguments: ['@router.no_access_checks', '@access_manager', '@current_user']
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index be5171e..4f72878 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -347,16 +347,10 @@ function template_preprocess_menu_local_task(&$variables) {
     '#options' => $link['localized_options'],
   );
 
-  if (!empty($link['href'])) {
-    // @todo - Remove this once all pages are converted to routes.
-    $variables['link']['#href'] = $link['href'];
-  }
-  else {
-    $variables['link'] += array(
-      '#route_name' => $link['route_name'],
-      '#route_parameters' => $link['route_parameters'],
-    );
-  }
+  $variables['link'] += array(
+    '#route_name' => $link['route_name'],
+    '#route_parameters' => $link['route_parameters'],
+  );
 }
 
 /**
@@ -394,10 +388,6 @@ function template_preprocess_menu_local_action(&$variables) {
       '#route_parameters' => $link['route_parameters'],
     );
   }
-  else {
-    // @todo - Remove this once all pages are converted to routes.
-    $variables['link']['#href'] = $link['href'];
-  }
 }
 
 /**
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index f136ca1..d937ebd 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -981,18 +981,19 @@ function template_preprocess_links(&$variables) {
         'route_name' => NULL,
         'route_parameters' => NULL,
         'ajax' => NULL,
+        'url' => NULL,
       );
 
       $li_attributes = array('class' => array());
       // Use the array key as class name.
       $li_attributes['class'][] = drupal_html_class($key);
 
-      $keys = array('title', 'href', 'route_name', 'route_parameters');
+      $keys = array('title', 'href', 'url', 'route_name', 'route_parameters');
       $link_element = array(
         '#type' => 'link',
         '#title' => $link['title'],
         '#options' => array_diff_key($link, array_combine($keys, $keys)),
-        '#href' => $link['href'],
+        '#url' => $link['url'],
         '#route_name' => $link['route_name'],
         '#route_parameters' => $link['route_parameters'],
         '#ajax' => $link['ajax'],
@@ -1000,7 +1001,7 @@ function template_preprocess_links(&$variables) {
 
       // Handle links and ensure that the active class is added on the LIs, but
       // only if the 'set_active_class' option is not empty.
-      if (isset($link['href']) || isset($link['route_name'])) {
+      if (isset($link['href']) || isset($link['route_name']) || isset($link['url'])) {
         if (!empty($variables['set_active_class'])) {
 
           // Also enable set_active_class for the contained link.
@@ -1018,11 +1019,11 @@ function template_preprocess_links(&$variables) {
             $li_attributes['data-drupal-link-query'] = Json::encode($query);
           }
 
-          if (isset($link['route_name'])) {
-            $path = \Drupal::service('url_generator')->getPathFromRoute($link['route_name'], $link['route_parameters']);
+          if (isset($link['url'])) {
+            $path = $link['url']->toString();
           }
-          else {
-            $path = $link['href'];
+          elseif (isset($link['route_name'])) {
+            $path = \Drupal::urlGenerator()->getPathFromRoute($link['route_name'], $link['route_parameters']);
           }
 
           // Add a "data-drupal-link-system-path" attribute to let the
diff --git a/core/lib/Drupal/Core/Entity/EntityForm.php b/core/lib/Drupal/Core/Entity/EntityForm.php
index 407b9d7..09d579d 100644
--- a/core/lib/Drupal/Core/Entity/EntityForm.php
+++ b/core/lib/Drupal/Core/Entity/EntityForm.php
@@ -222,7 +222,7 @@ protected function actions(array $form, FormStateInterface $form_state) {
           'class' => array('button', 'button--danger'),
         ),
       );
-      $actions['delete'] += $route_info->toRenderArray();
+      $actions['delete']['#url'] = $route_info;
     }
 
     return $actions;
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
index 58f6c6b..19311b5 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/MailToFormatter.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'email_mailto' formatter.
@@ -33,7 +34,7 @@ public function viewElements(FieldItemListInterface $items) {
       $elements[$delta] = array(
         '#type' => 'link',
         '#title' => $item->value,
-        '#href' => 'mailto:' . $item->value,
+        '#url' => Url::fromUri('mailto:' . $item->value),
       );
     }
 
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
index 762d5f3..d9d26b1 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldFormatter/UriLinkFormatter.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'uri_link' formatter.
@@ -30,11 +31,13 @@ public function viewElements(FieldItemListInterface $items) {
     $elements = array();
 
     foreach ($items as $delta => $item) {
-      $elements[$delta] = array(
-        '#type' => 'link',
-        '#href' => $item->value,
-        '#title' => $item->value,
-      );
+      if (!$item->isEmpty()) {
+        $elements[$delta] = array(
+          '#type' => 'link',
+          '#url' => Url::fromUri($item->value),
+          '#title' => $item->value,
+        );
+      }
     }
 
     return $elements;
diff --git a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
index a823cac..feb4587 100644
--- a/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
+++ b/core/lib/Drupal/Core/Field/Plugin/Field/FieldType/UriItem.php
@@ -61,4 +61,11 @@ public static function schema(FieldStorageDefinitionInterface $field_definition)
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function isEmpty() {
+    return !isset($this->values['value']) || $this->values['value'] === '';
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
index de98053..afa779a 100644
--- a/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
+++ b/core/lib/Drupal/Core/Form/ConfirmFormHelper.php
@@ -35,8 +35,7 @@ public static function buildCancelLink(ConfirmFormInterface $form, Request $requ
     if ($query->has('destination')) {
       $options = UrlHelper::parse($query->get('destination'));
       $link = array(
-        '#href' => $options['path'],
-        '#options' => $options,
+        '#url' => Url::fromUri('base://' . $options['path'], $options),
       );
     }
     // Check for a route-based cancel link.
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index 605a5ab..d34799c 100644
--- a/core/lib/Drupal/Core/Language/LanguageManager.php
+++ b/core/lib/Drupal/Core/Language/LanguageManager.php
@@ -11,6 +11,7 @@
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\StringTranslation\TranslationWrapper;
+use Drupal\Core\Url;
 
 /**
  * Class responsible for providing language support on language-unaware sites.
@@ -239,7 +240,7 @@ public function getFallbackCandidates(array $context = array()) {
   /**
    * {@inheritdoc}
    */
-  public function getLanguageSwitchLinks($type, $path) {
+  public function getLanguageSwitchLinks($type, Url $url) {
     return array();
   }
 
diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
index e163c0c..783496c 100644
--- a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
+++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Language;
 
 use Drupal\Core\StringTranslation\TranslationInterface;
+use Drupal\Core\Url;
 
 /**
  * Common interface for the language manager service.
@@ -181,13 +182,13 @@ public function getFallbackCandidates(array $context = array());
    *
    * @param string $type
    *   The language type.
-   * @param string $path
-   *   The internal path the switch links will be relative to.
+   * @param \Drupal\Core\Url $url
+   *   The URL the switch links will be relative to.
    *
    * @return array
    *   A keyed array of links ready to be themed.
    */
-  public function getLanguageSwitchLinks($type, $path);
+  public function getLanguageSwitchLinks($type, Url $url);
 
   /**
    * Sets the configuration override language.
diff --git a/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
index f6ee14b..bf86365 100644
--- a/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
+++ b/core/lib/Drupal/Core/Menu/Form/MenuLinkDefaultForm.php
@@ -113,7 +113,8 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta
     $link = array(
       '#type' => 'link',
       '#title' => $this->menuLink->getTitle(),
-    ) + $this->menuLink->getUrlObject()->toRenderArray();
+      '#url' => $this->menuLink->getUrlObject(),
+    );
     $form['path'] = array(
       'link' => $link,
       '#type' => 'item',
diff --git a/core/lib/Drupal/Core/Render/Element/Link.php b/core/lib/Drupal/Core/Render/Element/Link.php
index 474e9f5..06b8aef 100644
--- a/core/lib/Drupal/Core/Render/Element/Link.php
+++ b/core/lib/Drupal/Core/Render/Element/Link.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Render\Element;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\Url as UrlObject;
 
 /**
@@ -83,13 +84,18 @@ public static function preRenderLink($element) {
       $element = static::preRenderAjaxForm($element);
     }
 
-    if (isset($element['#route_name'])) {
+    if (!empty($element['#url'])) {
+      $element['#markup'] = \Drupal::l($element['#title'], $element['#url']->setOptions($element['#options']));
+    }
+    elseif (isset($element['#route_name'])) {
       $element['#route_parameters'] = empty($element['#route_parameters']) ? array() : $element['#route_parameters'];
       $element['#markup'] = \Drupal::l($element['#title'], new UrlObject($element['#route_name'], $element['#route_parameters'], $element['#options']));
     }
+    elseif (isset($element['#href'])) {
+      $element['#markup'] = \Drupal::l($element['#title'], UrlObject::fromUri($element['#href'], $element['#options']));
+    }
     else {
-      // @todo Convert to \Drupal::l(): https://www.drupal.org/node/2347045.
-      $element['#markup'] = _l($element['#title'], $element['#href'], $element['#options']);
+//      throw new \InvalidArgumentException(String::format('Missing #url, #route_name, or #href for @title', ['@title' => $element['#title']]));
     }
     return $element;
   }
diff --git a/core/lib/Drupal/Core/Render/Element/Table.php b/core/lib/Drupal/Core/Render/Element/Table.php
index d82fda8..830f72e 100644
--- a/core/lib/Drupal/Core/Render/Element/Table.php
+++ b/core/lib/Drupal/Core/Render/Element/Table.php
@@ -281,7 +281,8 @@ public static function validateTable(&$element, FormStateInterface $form_state,
    *   $form['table'][$row]['edit'] = array(
    *     '#type' => 'link',
    *     '#title' => t('Edit'),
-   *     '#href' => 'thing/' . $row . '/edit',
+   *     '#route_name' => 'entity.test_entity.edit_form',
+   *     '#route_parameters' => ['test_entity' => $row],
    *   );
    * }
    * @endcode
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index a6f0220..8361be7 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core;
 
+use Drupal\Component\Utility\String;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -203,7 +204,7 @@ public static function fromRoute($route_name, $route_parameters = array(), $opti
    */
   public static function fromUri($uri, $options = array()) {
     if (!parse_url($uri, PHP_URL_SCHEME)) {
-      throw new \InvalidArgumentException('You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.');
+      throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
     }
 
     $url = new static($uri, array(), $options);
@@ -480,20 +481,14 @@ public function toArray() {
    *   An associative array suitable for a render array.
    */
   public function toRenderArray() {
-    if ($this->unrouted) {
-      return array(
-        '#href' => $this->getUri(),
-        '#options' => $this->getOptions(),
-      );
-    }
-    else {
-      return array(
-        '#route_name' => $this->getRouteName(),
-        '#route_parameters' => $this->getRouteParameters(),
-        '#options' => $this->getOptions(),
-        '#access_callback' => array(get_class(), 'renderAccess'),
-      );
+    $render_array = [
+      '#url' => $this,
+      '#options' => $this->getOptions(),
+    ];
+    if (!$this->unrouted) {
+      $render_array['#access_callback'] = [get_class(), 'renderAccess'];
     }
+    return $render_array;
   }
 
   /**
@@ -543,7 +538,7 @@ public function access(AccountInterface $account = NULL) {
    *   Returns TRUE if the current user has access to the url, otherwise FALSE.
    */
   public static function renderAccess(array $element) {
-    return (new static($element['#route_name'], $element['#route_parameters'], $element['#options']))->access();
+    return $element['#url']->access();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index f567558..72a2b90 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -13,9 +13,11 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Link;
 use Drupal\Core\Path\AliasManagerInterface;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Url;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * Provides a class which generates a link with route names and parameters.
@@ -37,16 +39,36 @@ class LinkGenerator implements LinkGeneratorInterface {
   protected $moduleHandler;
 
   /**
+   * The path processor to convert the system path to one suitable for urls.
+   *
+   * @var \Drupal\Core\PathProcessor\OutboundPathProcessorInterface
+   */
+  protected $pathProcessor;
+
+  /**
+   * A request stack object.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
    * Constructs a LinkGenerator instance.
    *
    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
    *   The url generator.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
+   * @param \Drupal\Core\PathProcessor\OutboundPathProcessorInterface $path_processor
+   *   The path processor to convert the system path to one suitable for urls.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   A request stack object.
    */
-  public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler) {
+  public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, OutboundPathProcessorInterface $path_processor , RequestStack $request_stack) {
     $this->urlGenerator = $url_generator;
     $this->moduleHandler = $module_handler;
+    $this->pathProcessor = $path_processor;
+    $this->requestStack = $request_stack;
   }
 
   /**
@@ -110,8 +132,9 @@ public function generate($text, Url $url) {
       // Add a "data-drupal-link-system-path" attribute to let the
       // drupal.active-link library know the path in a standardized manner.
       if (!isset($variables['options']['attributes']['data-drupal-link-system-path'])) {
+        $options = [];
+        $system_path = $this->pathProcessor->processOutbound($url->getInternalPath(), $options, $this->requestStack->getCurrentRequest());
         // @todo System path is deprecated - use the route name and parameters.
-        $system_path = $url->getInternalPath();
         // Special case for the front page.
         $variables['options']['attributes']['data-drupal-link-system-path'] = $system_path == '' ? '<front>' : $system_path;
       }
diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
index 52d1d13..08d0083 100644
--- a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
+++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Utility;
 
+use Drupal\Component\Utility\String;
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -56,7 +57,7 @@ public function assemble($uri, array $options = []) {
       // UrlHelper::isExternal() only returns true for safe protocols.
       return $this->buildExternalUrl($uri, $options);
     }
-    throw new \InvalidArgumentException('You must use a valid URI scheme. Use base:// for a path e.g. to a Drupal file that needs the base path.');
+    throw new \InvalidArgumentException(String::format('The URI "@uri" is invalid. You must use a valid URI scheme. Use base:// for a path, e.g., to a Drupal file that needs the base path. Do not use this for internal paths controlled by Drupal.', ['@uri' => $uri]));
   }
 
   /**
diff --git a/core/modules/aggregator/src/FeedViewBuilder.php b/core/modules/aggregator/src/FeedViewBuilder.php
index 26523ab..caea077 100644
--- a/core/modules/aggregator/src/FeedViewBuilder.php
+++ b/core/modules/aggregator/src/FeedViewBuilder.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityViewBuilder;
 use Drupal\Core\Config\Config;
 use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -99,7 +100,7 @@ public function buildComponents(array &$build, array $entities, array $displays,
           $image_link = array(
             '#type' => 'link',
             '#title' => $link_title,
-            '#href' => $link_href,
+            '#url' => Url::fromUri($link_href),
             '#options' => array(
               'attributes' => array('class' => array('feed-image')),
               'html' => TRUE,
diff --git a/core/modules/aggregator/src/ItemsImporter.php b/core/modules/aggregator/src/ItemsImporter.php
index af19325..3098f08 100644
--- a/core/modules/aggregator/src/ItemsImporter.php
+++ b/core/modules/aggregator/src/ItemsImporter.php
@@ -120,7 +120,7 @@ public function refresh(FeedInterface $feed) {
       // Parse the feed.
       try {
         if ($this->parserManager->createInstance($this->config->get('parser'))->parse($feed)) {
-          if ($feed->getWebsiteUrl()) {
+          if (!$feed->getWebsiteUrl()) {
             $feed->setWebsiteUrl($feed->getUrl());
           }
           $feed->setHash($hash);
diff --git a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
index f28ff0d..2710ced 100644
--- a/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
+++ b/core/modules/aggregator/src/Plugin/Block/AggregatorFeedBlock.php
@@ -164,7 +164,7 @@ public function build() {
 
       $more_link = array(
         '#type' => 'more_link',
-        '#href' => 'aggregator/sources/' . $feed->id(),
+        '#url' => $feed->urlInfo(),
         '#attributes' => array('title' => $this->t("View this feed's recent news.")),
       );
       $read_more = drupal_render($more_link);
diff --git a/core/modules/aggregator/src/Tests/AddFeedTest.php b/core/modules/aggregator/src/Tests/AddFeedTest.php
index 5b15d02..67fecf8 100644
--- a/core/modules/aggregator/src/Tests/AddFeedTest.php
+++ b/core/modules/aggregator/src/Tests/AddFeedTest.php
@@ -27,7 +27,7 @@ function testAddFeed() {
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, 'Feed source exists.');
     $this->assertText($feed->label(), 'Page title');
-    $this->assertText($feed->label());
+    $this->assertRaw($feed->getWebsiteUrl());
 
     // Delete feed.
     $this->deleteFeed($feed);
@@ -54,7 +54,6 @@ function testAddLongFeed() {
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, 'Long URL feed source exists.');
     $this->assertText($feed->label(), 'Page title');
-    $this->assertText($feed->label());
 
     // Delete feeds.
     $this->deleteFeed($feed);
diff --git a/core/modules/aggregator/src/Tests/AggregatorTestBase.php b/core/modules/aggregator/src/Tests/AggregatorTestBase.php
index 57fcf88..dd1bbff 100644
--- a/core/modules/aggregator/src/Tests/AggregatorTestBase.php
+++ b/core/modules/aggregator/src/Tests/AggregatorTestBase.php
@@ -58,7 +58,10 @@ function createFeed($feed_url = NULL, array $edit = array()) {
 
     $fid = db_query("SELECT fid FROM {aggregator_feed} WHERE title = :title AND url = :url", array(':title' => $edit['title[0][value]'], ':url' => $edit['url[0][value]']))->fetchField();
     $this->assertTrue(!empty($fid), 'The feed found in database.');
-    return Feed::load($fid);
+    if ($feed = Feed::load($fid)) {
+      $feed->refreshItems();
+    }
+    return $feed;
   }
 
   /**
diff --git a/core/modules/aggregator/src/Tests/FeedParserTest.php b/core/modules/aggregator/src/Tests/FeedParserTest.php
index a6d44c7..bfda92c 100644
--- a/core/modules/aggregator/src/Tests/FeedParserTest.php
+++ b/core/modules/aggregator/src/Tests/FeedParserTest.php
@@ -33,7 +33,6 @@ protected function setUp() {
    */
   function testRSS091Sample() {
     $feed = $this->createFeed($this->getRSS091Sample());
-    $feed->refreshItems();
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->label())));
     $this->assertText('First example feed item title');
@@ -55,7 +54,6 @@ function testRSS091Sample() {
    */
   function testAtomSample() {
     $feed = $this->createFeed($this->getAtomSample());
-    $feed->refreshItems();
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->label())));
     $this->assertText('Atom-Powered Robots Run Amok');
@@ -69,7 +67,6 @@ function testAtomSample() {
    */
   function testHtmlEntitiesSample() {
     $feed = $this->createFeed($this->getHtmlEntitiesSample());
-    $feed->refreshItems();
     $this->drupalGet('aggregator/sources/' . $feed->id());
     $this->assertResponse(200, format_string('Feed %name exists.', array('%name' => $feed->label())));
     $this->assertRaw("Quote&quot; Amp&amp;");
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index cbf56ab..693e90c 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -93,10 +93,15 @@ function block_page_build(&$page) {
     $page['page_top']['backlink'] = array(
       '#type' => 'link',
       '#title' => t('Exit block region demonstration'),
-      '#href' => 'admin/structure/block' . (\Drupal::config('system.theme')->get('default') == $theme ? '' : '/list/' . $theme),
       '#options' => array('attributes' => array('class' => array('block-demo-backlink'))),
       '#weight' => -10,
     );
+    if (\Drupal::config('system.theme')->get('default') == $theme) {
+      $page['page_top']['backlink']['#url'] = Url::fromRoute('block.admin_display_theme', ['theme' => $theme]);
+    }
+    else {
+      $page['page_top']['backlink']['#url'] = Url::fromRoute('block.admin_display');
+    }
   }
 }
 
diff --git a/core/modules/block/src/Tests/BlockTest.php b/core/modules/block/src/Tests/BlockTest.php
index 1996c9b..95d4fdb 100644
--- a/core/modules/block/src/Tests/BlockTest.php
+++ b/core/modules/block/src/Tests/BlockTest.php
@@ -146,7 +146,7 @@ function testBlock() {
     $this->assertRaw(t('Are you sure you want to delete the block %name?', array('%name' => $block->label())));
     $this->drupalPostForm(NULL, array(), t('Delete'));
     $this->assertRaw(t('The block %name has been removed.', array('%name' => $block->label())));
-    $this->assertUrl('admin');
+    $this->assertUrl('admin/structure/block');
     $this->assertNoRaw($block->id());
   }
 
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index 469267c..ca559ba 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -45,7 +45,8 @@ function theme_book_admin_table($variables) {
     $links = array();
     $links['view'] = array(
       'title' => t('View'),
-      'href' => $href,
+      'route_name' => 'entity.node.canonical',
+      'route_parameters' => ['node' => $nid],
     );
     if ($access) {
       $links['edit'] = array(
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index 6b631c7..0240cc4 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -12,6 +12,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
 use Drupal\node\NodeInterface;
 use Drupal\node\NodeTypeInterface;
 use Drupal\node\Entity\Node;
@@ -108,7 +109,8 @@ function book_node_links_alter(array &$node_links, NodeInterface $node, array &$
         if (($account->hasPermission('add content to books') || $account->hasPermission('administer book outlines')) && $access_control_handler->createAccess($child_type) && $node->isPublished() && $node->book['depth'] < BookManager::BOOK_MAX_DEPTH) {
           $links['book_add_child'] = array(
             'title' => t('Add child page'),
-            'href' => 'node/add/' . $child_type,
+            'route_name' => 'node.add',
+            'route_parameters' => ['node_type' => $child_type],
             'query' => array('parent' => $node->id()),
           );
         }
@@ -116,7 +118,11 @@ function book_node_links_alter(array &$node_links, NodeInterface $node, array &$
         if ($account->hasPermission('access printer-friendly version')) {
           $links['book_printer'] = array(
             'title' => t('Printer-friendly version'),
-            'href' => 'book/export/html/' . $node->id(),
+            'route_name' => 'book.export',
+            'route_parameters' => [
+              'type' => 'html',
+              'node' => $node->id(),
+            ],
             'attributes' => array('title' => t('Show a printer-friendly version of this book page and its sub-pages.'))
           );
         }
diff --git a/core/modules/comment/src/CommentLinkBuilder.php b/core/modules/comment/src/CommentLinkBuilder.php
index 9726712..b4e4a13 100644
--- a/core/modules/comment/src/CommentLinkBuilder.php
+++ b/core/modules/comment/src/CommentLinkBuilder.php
@@ -114,7 +114,7 @@ public function buildCommentedEntityLinks(FieldableEntityInterface $entity, arra
               if ($this->moduleHandler->moduleExists('history')) {
                 $links['comment-new-comments'] = array(
                   'title' => '',
-                  'href' => '',
+                  'route_name' => '<current>',
                   'attributes' => array(
                     'class' => 'hidden',
                     'title' => $this->t('Jump to the first new comment of this posting.'),
diff --git a/core/modules/comment/src/CommentViewBuilder.php b/core/modules/comment/src/CommentViewBuilder.php
index 524349d..da9ebbc 100644
--- a/core/modules/comment/src/CommentViewBuilder.php
+++ b/core/modules/comment/src/CommentViewBuilder.php
@@ -246,7 +246,7 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
       if ($entity->access('delete')) {
         $links['comment-delete'] = array(
           'title' => t('Delete'),
-          'href' => "comment/{$entity->id()}/delete",
+          'url' => $entity->urlInfo('delete-form'),
           'html' => TRUE,
         );
       }
@@ -254,14 +254,20 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
       if ($entity->access('update')) {
         $links['comment-edit'] = array(
           'title' => t('Edit'),
-          'href' => "comment/{$entity->id()}/edit",
+          'url' => $entity->urlInfo('edit-form'),
           'html' => TRUE,
         );
       }
       if ($entity->access('create')) {
         $links['comment-reply'] = array(
           'title' => t('Reply'),
-          'href' => "comment/reply/{$entity->getCommentedEntityTypeId()}/{$entity->getCommentedEntityId()}/{$entity->getFieldName()}/{$entity->id()}",
+          'route_name' => 'comment.reply',
+          'route_parameters' => [
+            'entity_type' => $entity->getCommentedEntityTypeId(),
+            'entity' => $entity->getCommentedEntityId(),
+            'field_name' => $entity->getFieldName(),
+            'pid' => $entity->id(),
+          ],
           'html' => TRUE,
         );
       }
@@ -283,7 +289,7 @@ protected static function buildLinks(CommentInterface $entity, EntityInterface $
     if (\Drupal::moduleHandler()->moduleExists('content_translation') && content_translation_translate_access($entity)->isAllowed()) {
       $links['comment-translations'] = array(
         'title' => t('Translate'),
-        'href' => 'comment/' . $entity->id() . '/translations',
+        'url' => $entity->urlInfo('drupal:content-translation-overview'),
         'html' => TRUE,
       );
     }
diff --git a/core/modules/comment/src/Form/CommentAdminOverview.php b/core/modules/comment/src/Form/CommentAdminOverview.php
index 723241a..68311d7 100644
--- a/core/modules/comment/src/Form/CommentAdminOverview.php
+++ b/core/modules/comment/src/Form/CommentAdminOverview.php
@@ -200,7 +200,8 @@ public function buildForm(array $form, FormStateInterface $form_state, $type = '
           'data' => array(
             '#type' => 'link',
             '#title' => $comment->getSubject(),
-          ) + $comment_permalink->toRenderArray(),
+            '#url' => $comment_permalink,
+          ),
         ),
         'author' => drupal_render($username),
         'posted_in' => array(
diff --git a/core/modules/config/src/Controller/ConfigController.php b/core/modules/config/src/Controller/ConfigController.php
index 48b31df..8eb713f 100644
--- a/core/modules/config/src/Controller/ConfigController.php
+++ b/core/modules/config/src/Controller/ConfigController.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Diff\DiffFormatter;
+use Drupal\Core\Url;
 use Drupal\system\FileDownloadController;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -156,7 +157,7 @@ public function diff($source_name, $target_name = NULL, $collection = NULL) {
         ),
       ),
       '#title' => "Back to 'Synchronize configuration' page.",
-      '#href' => 'admin/config/development/configuration',
+      '#url' => Url::fromRoute('config.sync'),
     );
 
     return $build;
diff --git a/core/modules/config/src/Form/ConfigSync.php b/core/modules/config/src/Form/ConfigSync.php
index f73de96..16bceed 100644
--- a/core/modules/config/src/Form/ConfigSync.php
+++ b/core/modules/config/src/Form/ConfigSync.php
@@ -20,7 +20,6 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Lock\LockBackendInterface;
 use Drupal\Core\Config\StorageComparer;
-use Drupal\Core\Routing\UrlGeneratorInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -72,13 +71,6 @@ class ConfigSync extends FormBase {
   protected $configManager;
 
   /**
-   * URL generator service.
-   *
-   * @var \Drupal\Core\Routing\UrlGeneratorInterface
-   */
-  protected $urlGenerator;
-
-  /**
    * The typed config manager.
    *
    * @var \Drupal\Core\Config\TypedConfigManagerInterface
@@ -114,8 +106,6 @@ class ConfigSync extends FormBase {
    *   Event dispatcher.
    * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
    *   Configuration manager.
-   * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
-   *   The url generator service.
    * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
    *   The typed configuration manager.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -123,14 +113,13 @@ class ConfigSync extends FormBase {
    * @param \Drupal\Core\Extension\ThemeHandlerInterface $theme_handler
    *   The theme handler
    */
-  public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, UrlGeneratorInterface $url_generator, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
+  public function __construct(StorageInterface $staging_storage, StorageInterface $active_storage, StorageInterface $snapshot_storage, LockBackendInterface $lock, EventDispatcherInterface $event_dispatcher, ConfigManagerInterface $config_manager, TypedConfigManagerInterface $typed_config, ModuleHandlerInterface $module_handler, ThemeHandlerInterface $theme_handler) {
     $this->stagingStorage = $staging_storage;
     $this->activeStorage = $active_storage;
     $this->snapshotStorage = $snapshot_storage;
     $this->lock = $lock;
     $this->eventDispatcher = $event_dispatcher;
     $this->configManager = $config_manager;
-    $this->urlGenerator = $url_generator;
     $this->typedConfigManager = $typed_config;
     $this->moduleHandler = $module_handler;
     $this->themeHandler = $theme_handler;
@@ -147,7 +136,6 @@ public static function create(ContainerInterface $container) {
       $container->get('lock'),
       $container->get('event_dispatcher'),
       $container->get('config.manager'),
-      $container->get('url_generator'),
       $container->get('config.typed'),
       $container->get('module_handler'),
       $container->get('theme_handler')
@@ -268,15 +256,16 @@ public function buildForm(array $form, FormStateInterface $form_state) {
             $route_options = array('source_name' => $config_name);
           }
           if ($collection != StorageInterface::DEFAULT_COLLECTION) {
+            $route_name = 'config.diff_collection';
             $route_options['collection'] = $collection;
-            $href = $this->urlGenerator->getPathFromRoute('config.diff_collection', $route_options);
           }
           else {
-            $href = $this->urlGenerator->getPathFromRoute('config.diff', $route_options);
+            $route_name = 'config.diff';
           }
           $links['view_diff'] = array(
             'title' => $this->t('View differences'),
-            'href' => $href,
+            'route_name' => $route_name,
+            'route_parameters' => $route_options,
             'attributes' => array(
               'class' => array('use-ajax'),
               'data-accepts' => 'application/vnd.drupal-modal',
diff --git a/core/modules/config_translation/src/ConfigEntityMapper.php b/core/modules/config_translation/src/ConfigEntityMapper.php
index ce66ff6..9265d33 100644
--- a/core/modules/config_translation/src/ConfigEntityMapper.php
+++ b/core/modules/config_translation/src/ConfigEntityMapper.php
@@ -216,7 +216,10 @@ public function getOperations() {
     return array(
       'list' => array(
         'title' => $this->t('List'),
-        'href' => 'admin/config/regional/config-translation/' . $this->getPluginId(),
+        'route_name' => 'config_translation.entity_list',
+        'route_parameters' => [
+          'mapper_id' => $this->getPluginId(),
+        ],
       ),
     );
   }
diff --git a/core/modules/config_translation/src/ConfigNamesMapper.php b/core/modules/config_translation/src/ConfigNamesMapper.php
index 57811d9..9e57344 100644
--- a/core/modules/config_translation/src/ConfigNamesMapper.php
+++ b/core/modules/config_translation/src/ConfigNamesMapper.php
@@ -15,6 +15,7 @@
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Url;
 use Drupal\locale\LocaleConfigManager;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -200,7 +201,7 @@ protected function processRoute(Route $route) {
    * {@inheritdoc}
    */
   public function getBasePath() {
-    return $this->getPathFromRoute($this->getBaseRoute(), $this->getBaseRouteParameters());
+    return Url::fromRoute($this->getBaseRouteName(), $this->getBaseRouteParameters())->toString();
   }
 
   /**
@@ -237,7 +238,7 @@ public function getOverviewRoute() {
    * {@inheritdoc}
    */
   public function getOverviewPath() {
-    return $this->getPathFromRoute($this->getOverviewRoute(), $this->getOverviewRouteParameters());
+    return Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters())->toString();
   }
 
   /**
@@ -335,25 +336,6 @@ public function getDeleteRoute() {
   }
 
   /**
-   * Gets the path for a certain route, given a set of route parameters.
-   *
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route object.
-   * @param array $parameters
-   *   An array of route parameters.
-   *
-   * @return string
-   *   Processed path with placeholders replaced.
-   */
-  public function getPathFromRoute(Route $route, array $parameters) {
-    $path = $route->getPath();
-    foreach ($parameters as $key => $value) {
-      $path = str_replace('{' . $key . '}', $value, $path);
-    }
-    return $path;
-  }
-
-  /**
    * {@inheritdoc}
    */
   public function getConfigNames() {
@@ -492,7 +474,7 @@ public function getOperations() {
     return array(
       'translate' => array(
         'title' => $this->t('Translate'),
-        'href' => $this->getOverviewPath(),
+        'url' => Url::fromRoute($this->getOverviewRouteName(), $this->getOverviewRouteParameters()),
       ),
     );
   }
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
index 69828f6..73cc7bb 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigEntityMapperTest.php
@@ -188,8 +188,11 @@ public function testGetOperations() {
     $expected = array(
       'list' => array(
         'title' => 'List',
-        'href' => 'admin/config/regional/config-translation/configurable_language',
-      )
+        'route_name' => 'config_translation.entity_list',
+        'route_parameters' => [
+          'mapper_id' => 'configurable_language',
+        ],
+      ),
     );
 
     $this->assertSame($expected, $result);
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
index 5b19122..7c26ed5 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
@@ -9,7 +9,9 @@
 
 use Drupal\config_translation\ConfigNamesMapper;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Url;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\HttpFoundation\Request;
@@ -72,6 +74,13 @@ class ConfigNamesMapperTest extends UnitTestCase {
    */
   protected $routeProvider;
 
+  /**
+   * The mocked URL generator.
+   *
+   * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $urlGenerator;
+
   protected function setUp() {
     $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
 
@@ -91,6 +100,11 @@ protected function setUp() {
 
     $this->configMapperManager = $this->getMock('Drupal\config_translation\ConfigMapperManagerInterface');
 
+    $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+    $container = new ContainerBuilder();
+    $container->set('url_generator', $this->urlGenerator);
+    \Drupal::setContainer($container);
+
     $this->baseRoute = new Route('/admin/config/system/site-information');
 
     $this->routeProvider
@@ -147,6 +161,10 @@ public function testGetBaseRoute() {
    * Tests ConfigNamesMapper::getBasePath().
    */
   public function testGetBasePath() {
+    $this->urlGenerator->expects($this->once())
+      ->method('generateFromRoute')
+      ->with('system.site_information_settings', [], [])
+      ->willReturn('/admin/config/system/site-information');
     $result = $this->configNamesMapper->getBasePath();
     $this->assertSame('/admin/config/system/site-information', $result);
   }
@@ -189,6 +207,11 @@ public function testGetOverviewRoute() {
    * Tests ConfigNamesMapper::getOverviewPath().
    */
   public function testGetOverviewPath() {
+    $this->urlGenerator->expects($this->once())
+      ->method('generateFromRoute')
+      ->with('config_translation.item.overview.system.site_information_settings', [], [])
+      ->willReturn('/admin/config/system/site-information/translate');
+
     $result = $this->configNamesMapper->getOverviewPath();
     $this->assertSame('/admin/config/system/site-information/translate', $result);
   }
@@ -605,7 +628,7 @@ public function testGetOperations() {
     $expected = array(
       'translate' => array(
         'title' => 'Translate',
-        'href' => '/admin/config/system/site-information/translate',
+        'url' => Url::fromRoute('config_translation.item.overview.system.site_information_settings'),
       ),
     );
     $result = $this->configNamesMapper->getOperations();
diff --git a/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php b/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
index 154759f..e50e70d 100644
--- a/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
+++ b/core/modules/entity_reference/src/Plugin/Field/FieldFormatter/EntityReferenceLabelFormatter.php
@@ -76,11 +76,14 @@ public function viewElements(FieldItemListInterface $items) {
           $elements[$delta] = array(
             '#type' => 'link',
             '#title' => $label,
-          ) + $uri->toRenderArray();
+            '#url' => $uri,
+          );
 
           if (!empty($item->_attributes)) {
-            $elements[$delta]['#options'] += array('attributes' => array());
-            $elements[$delta]['#options']['attributes'] += $item->_attributes;
+            $options = $elements[$delta]['#url']->getOptions();
+            $options += array('attributes' => array());
+            $options['attributes'] += $item->_attributes;
+            $elements[$delta]['#url']->setOptions($options);
             // Unset field item attributes since they have been included in the
             // formatter output and shouldn't be rendered in the field template.
             unset($item->_attributes);
diff --git a/core/modules/field_ui/src/EntityDisplayModeListBuilder.php b/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
index 2a73834..6056508 100644
--- a/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
+++ b/core/modules/field_ui/src/EntityDisplayModeListBuilder.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -118,7 +119,7 @@ public function render() {
       $table['#rows']['_add_new'][] = array(
         'data' => array(
           '#type' => 'link',
-          '#href' => "admin/structure/display-modes/$short_type/add/$entity_type",
+          '#url' => Url::fromRoute($short_type == 'view' ? 'field_ui.entity_view_mode_add_type' : 'field_ui.entity_form_mode_add_type', ['entity_type_id' => $entity_type]),
           '#title' => t('Add new %label @entity-type', array('%label' => $this->entityTypes[$entity_type]->getLabel(), '@entity-type' => $this->entityType->getLowercaseLabel())),
           '#options' => array(
             'html' => TRUE,
diff --git a/core/modules/field_ui/src/Tests/ManageFieldsTest.php b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
index ff21395..2379899 100644
--- a/core/modules/field_ui/src/Tests/ManageFieldsTest.php
+++ b/core/modules/field_ui/src/Tests/ManageFieldsTest.php
@@ -106,7 +106,7 @@ function manageFieldsPage($type = '') {
     $url = base_path() . "admin/structure/types/manage/$type/fields/node.$type.body";
     $this->assertIdentical($url, (string) $result[0]['href']);
     $this->assertIdentical("$url/storage", (string) $result[1]['href']);
-    $this->assertIdentical("$url/delete", (string) $result[3]['href']);
+    $this->assertIdentical("$url/delete", (string) $result[2]['href']);
   }
 
   /**
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index b4bf306..4d9833a 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -120,7 +120,8 @@ function forum_menu_local_tasks(&$data, $route_name) {
           '#theme' => 'menu_local_action',
           '#link' => array(
             'title' => t('Add new @node_type', array('@node_type' => entity_load('node_type', $type)->label())),
-            'href' => 'node/add/' . $type,
+            'route_name' => 'node.add',
+            'route_parameters' => ['node_type' => $type],
           ),
         );
         if ($forum_term && $forum_term->bundle() == $vid) {
diff --git a/core/modules/forum/src/Form/Overview.php b/core/modules/forum/src/Form/Overview.php
index eb05b3a..d897881 100644
--- a/core/modules/forum/src/Form/Overview.php
+++ b/core/modules/forum/src/Form/Overview.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Drupal\taxonomy\Form\OverviewTerms;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -65,7 +66,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     foreach (Element::children($form['terms']) as $key) {
       if (isset($form['terms'][$key]['#term'])) {
         $term = $form['terms'][$key]['#term'];
-        $form['terms'][$key]['term']['#href'] = 'forum/' . $term->id();
+        $form['terms'][$key]['term']['#url'] = Url::fromRoute('forum.page', ['taxonomy_term' => $term->id()]);
         unset($form['terms'][$key]['operations']['#links']['delete']);
         if (!empty($term->forum_container->value)) {
           $form['terms'][$key]['operations']['#links']['edit']['title'] = $this->t('edit container');
diff --git a/core/modules/forum/src/Plugin/Block/ForumBlockBase.php b/core/modules/forum/src/Plugin/Block/ForumBlockBase.php
index f480892..edd7ac6 100644
--- a/core/modules/forum/src/Plugin/Block/ForumBlockBase.php
+++ b/core/modules/forum/src/Plugin/Block/ForumBlockBase.php
@@ -27,7 +27,7 @@ public function build() {
       $elements['forum_list'] = $node_title_list;
       $elements['forum_more'] = array(
         '#type' => 'more_link',
-        '#href' => 'forum',
+        '#route_name' => 'forum.index',
         '#attributes' => array('title' => $this->t('Read the latest forum topics.')),
       );
     }
diff --git a/core/modules/image/src/Form/ImageEffectFormBase.php b/core/modules/image/src/Form/ImageEffectFormBase.php
index 52d5b2c..addbcf6 100644
--- a/core/modules/image/src/Form/ImageEffectFormBase.php
+++ b/core/modules/image/src/Form/ImageEffectFormBase.php
@@ -96,7 +96,8 @@ public function buildForm(array $form, FormStateInterface $form_state, ImageStyl
     $form['actions']['cancel'] = array(
       '#type' => 'link',
       '#title' => $this->t('Cancel'),
-    ) + $this->imageStyle->urlInfo('edit-form')->toRenderArray();
+      '#url' => $this->imageStyle->urlInfo('edit-form'),
+    );
     return $form;
   }
 
diff --git a/core/modules/image/src/Form/ImageStyleEditForm.php b/core/modules/image/src/Form/ImageStyleEditForm.php
index 8703ee6..1b28d7f 100644
--- a/core/modules/image/src/Form/ImageStyleEditForm.php
+++ b/core/modules/image/src/Form/ImageStyleEditForm.php
@@ -126,12 +126,20 @@ public function form(array $form, FormStateInterface $form_state) {
       if ($is_configurable) {
         $links['edit'] = array(
           'title' => $this->t('Edit'),
-          'href' => 'admin/config/media/image-styles/manage/' . $this->entity->id() . '/effects/' . $key,
+          'route_name' => 'image.effect_edit_form',
+          'route_parameters' => [
+            'image_style' => $this->entity->id(),
+            'image_effect' => $key,
+          ],
         );
       }
       $links['delete'] = array(
         'title' => $this->t('Delete'),
-        'href' => 'admin/config/media/image-styles/manage/' . $this->entity->id() . '/effects/' . $key . '/delete',
+        'route_name' => 'image.effect_delete',
+        'route_parameters' => [
+          'image_style' => $this->entity->id(),
+          'image_effect' => $key,
+        ],
       );
       $form['effects'][$key]['operations'] = array(
         '#type' => 'operations',
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index d5e5b7f..b060d35 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -113,7 +113,8 @@ function theme_language_negotiation_configure_browser_form_table($variables) {
     $links = array();
     $links['delete'] = array(
       'title' => t('Delete'),
-      'href' => "admin/config/regional/language/detection/browser/delete/$key",
+      'route_name' => 'language.negotiation_browser_delete',
+      'route_parameters' => ['browser_langcode' => $key],
       'attributes' => array(
         'class' => array('image-style-link'),
       ),
diff --git a/core/modules/language/src/ConfigurableLanguageManager.php b/core/modules/language/src/ConfigurableLanguageManager.php
index 6699ab0..6345029 100644
--- a/core/modules/language/src/ConfigurableLanguageManager.php
+++ b/core/modules/language/src/ConfigurableLanguageManager.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageDefault;
 use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Url;
 use Drupal\language\Config\LanguageConfigFactoryOverrideInterface;
 use Drupal\language\Entity\ConfigurableLanguage;
 use Symfony\Component\HttpFoundation\Request;
@@ -395,7 +396,7 @@ public function getFallbackCandidates(array $context = array()) {
   /**
    * {@inheritdoc}
    */
-  public function getLanguageSwitchLinks($type, $path) {
+  public function getLanguageSwitchLinks($type, Url $url) {
     $links = FALSE;
 
     if ($this->negotiator) {
@@ -403,7 +404,7 @@ public function getLanguageSwitchLinks($type, $path) {
         $reflector = new \ReflectionClass($method['class']);
 
         if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) {
-          $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $path);
+          $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $url);
 
           if (!empty($result)) {
             // Allow modules to provide translations for specific links.
diff --git a/core/modules/language/src/Form/NegotiationConfigureForm.php b/core/modules/language/src/Form/NegotiationConfigureForm.php
index 83eb567..5b2b471 100644
--- a/core/modules/language/src/Form/NegotiationConfigureForm.php
+++ b/core/modules/language/src/Form/NegotiationConfigureForm.php
@@ -295,10 +295,10 @@ protected function configureFormTable(array &$form, $type)  {
         $table_form['description'][$method_id] = array('#markup' => Xss::filterAdmin($method['description']));
 
         $config_op = array();
-        if (isset($method['config_path'])) {
+        if (isset($method['config_route_name'])) {
           $config_op['configure'] = array(
             'title' => $this->t('Configure'),
-            'href' => $method['config_path'],
+            'route_name' => $method['config_route_name'],
           );
           // If there is at least one operation enabled show the operation
           // column.
diff --git a/core/modules/language/src/LanguageSwitcherInterface.php b/core/modules/language/src/LanguageSwitcherInterface.php
index d6edd5b..0550dd4 100644
--- a/core/modules/language/src/LanguageSwitcherInterface.php
+++ b/core/modules/language/src/LanguageSwitcherInterface.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\language;
 
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -21,12 +22,12 @@
    *   The current request.
    * @param string $type
    *   The language type.
-   * @param string $path
-   *   The path links should point to.
+   * @param \Drupal\Core\Url $url
+   *   The URL the switch links will be relative to.
    *
    * @return array
    *   An array of link arrays keyed by language code.
    */
-  public function getLanguageSwitchLinks(Request $request, $type, $path);
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url);
 
 }
diff --git a/core/modules/language/src/Plugin/Block/LanguageBlock.php b/core/modules/language/src/Plugin/Block/LanguageBlock.php
index a3aba84..0502d2d 100644
--- a/core/modules/language/src/Plugin/Block/LanguageBlock.php
+++ b/core/modules/language/src/Plugin/Block/LanguageBlock.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -75,9 +76,9 @@ protected function blockAccess(AccountInterface $account) {
    */
   public function build() {
     $build = array();
-    $path = drupal_is_front_page() ? '<front>' : current_path();
+    $route_name = drupal_is_front_page() ? '<front>' : '<current>';
     $type = $this->getDerivativeId();
-    $links = $this->languageManager->getLanguageSwitchLinks($type, $path);
+    $links = $this->languageManager->getLanguageSwitchLinks($type, Url::fromRoute($route_name));
 
     if (isset($links->links)) {
       $build = array(
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
index 0ce2c86..e9fc38e 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -20,7 +20,7 @@
  *   cache = 0,
  *   name = @Translation("Browser"),
  *   description = @Translation("Language from the browser's language settings."),
- *   config_path = "admin/config/regional/language/detection/browser"
+ *   config_route_name = "language.negotiation_browser"
  * )
  */
 class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase {
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
index 4819abf..c4cd150 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -18,7 +18,7 @@
  *   weight = 12,
  *   name = @Translation("Selected language"),
  *   description = @Translation("Language based on a selected language."),
- *   config_path = "admin/config/regional/language/detection/selected"
+ *   config_route_name = "language.negotiation_selected"
  * )
  */
 class LanguageNegotiationSelected extends LanguageNegotiationMethodBase {
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
index dc5617d..7f0085d 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Url;
 use Drupal\language\LanguageNegotiationMethodBase;
 use Drupal\language\LanguageSwitcherInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -21,7 +22,7 @@
  *   weight = -6,
  *   name = @Translation("Session"),
  *   description = @Translation("Language from a request/session parameter."),
- *   config_path = "admin/config/regional/language/detection/session"
+ *   config_route_name = "language.negotiation_session"
  * )
  */
 class LanguageNegotiationSession extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface {
@@ -121,7 +122,7 @@ public function processOutbound($path, &$options = array(), Request $request = N
   /**
    * {@inheritdoc}
    */
-  function getLanguageSwitchLinks(Request $request, $type, $path) {
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = array();
     $config = $this->config->get('language.negotiation')->get('session');
     $param = $config['parameter'];
@@ -132,7 +133,7 @@ function getLanguageSwitchLinks(Request $request, $type, $path) {
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $langcode = $language->id;
       $links[$langcode] = array(
-        'href' => $path,
+        'url' => $url,
         'title' => $language->getName(),
         'attributes' => array('class' => array('language-link')),
         'query' => $query,
diff --git a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
index be9f915..0b46727 100644
--- a/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
+++ b/core/modules/language/src/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\Core\Url;
 use Drupal\language\LanguageNegotiationMethodBase;
 use Drupal\language\LanguageSwitcherInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -25,7 +26,7 @@
  *   weight = -8,
  *   name = @Translation("URL"),
  *   description = @Translation("Language from the URL (Path prefix or domain)."),
- *   config_path = "admin/config/regional/language/detection/url"
+ *   config_route_name = "language.negotiation_url"
  * )
  */
 class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {
@@ -187,12 +188,12 @@ public function processOutbound($path, &$options = array(), Request $request = N
   /**
    * {@inheritdoc}
    */
-  function getLanguageSwitchLinks(Request $request, $type, $path) {
+  public function getLanguageSwitchLinks(Request $request, $type, Url $url) {
     $links = array();
 
     foreach ($this->languageManager->getNativeLanguages() as $language) {
       $links[$language->id] = array(
-        'href' => $path,
+        'url' => $url,
         'title' => $language->getName(),
         'language' => $language,
         'attributes' => array('class' => array('language-link')),
diff --git a/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php b/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php
index 2935b7d..a5cb033 100644
--- a/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php
+++ b/core/modules/language/tests/language_test/src/Controller/LanguageTestController.php
@@ -62,7 +62,7 @@ public function typeLinkActiveClass() {
       'no_language' => array(
         '#type' => 'link',
         '#title' => t('Link to the current path with no langcode provided.'),
-        '#href' => current_path(),
+        '#route_name' => '<current>',
         '#options' => array(
           'attributes' => array(
             'id' => 'no_lang_link',
@@ -73,7 +73,7 @@ public function typeLinkActiveClass() {
       'fr' => array(
         '#type' => 'link',
         '#title' => t('Link to a French version of the current path.'),
-        '#href' => current_path(),
+        '#route_name' => '<current>',
         '#options' => array(
           'language' => $languages['fr'],
           'attributes' => array(
@@ -85,7 +85,7 @@ public function typeLinkActiveClass() {
       'en' => array(
         '#type' => 'link',
         '#title' => t('Link to an English version of the current path.'),
-        '#href' => current_path(),
+        '#route_name' => '<current>',
         '#options' => array(
           'language' => $languages['en'],
           'attributes' => array(
diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index ca753f8..3aabb67 100644
--- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -163,13 +163,7 @@ public function viewElements(FieldItemListInterface $items) {
           '#title' => $link_title,
           '#options' => $url->getOptions(),
         );
-        if ($url->isExternal()) {
-          $element[$delta]['#href'] = $url->getUri();
-        }
-        else {
-          $element[$delta]['#route_name'] = $url->getRouteName();
-          $element[$delta]['#route_parameters'] = $url->getRouteParameters();
-        }
+        $element[$delta]['#url'] = $url;
 
         if (!empty($item->_attributes)) {
           $element[$delta]['#options'] += array ('attributes' => array());
diff --git a/core/modules/node/src/Form/NodePreviewForm.php b/core/modules/node/src/Form/NodePreviewForm.php
index 81fd61c..52f0267 100644
--- a/core/modules/node/src/Form/NodePreviewForm.php
+++ b/core/modules/node/src/Form/NodePreviewForm.php
@@ -13,6 +13,7 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -81,7 +82,7 @@ public function buildForm(array $form, FormStateInterface $form_state, EntityInt
     $form['backlink'] = array(
       '#type' => 'link',
       '#title' => $this->t('Back to content editing'),
-      '#href' => $node->isNew() ? 'node/add/' . $node->bundle() :  'node/' . $node->id() . '/edit',
+      '#url' => $node->isNew() ? Url::fromRoute('node.add', ['node_type' => $node->bundle()]) : $node->urlInfo('edit-form'),
       '#options' => array('attributes' => array('class' => array('node-preview-backlink'))) + $query_options,
     );
 
diff --git a/core/modules/node/src/NodeListBuilder.php b/core/modules/node/src/NodeListBuilder.php
index c78915d..606da79 100644
--- a/core/modules/node/src/NodeListBuilder.php
+++ b/core/modules/node/src/NodeListBuilder.php
@@ -105,7 +105,8 @@ public function buildRow(EntityInterface $entity) {
       '#type' => 'link',
       '#title' => $entity->label(),
       '#suffix' => ' ' . drupal_render($mark),
-    ) + $uri->toRenderArray();
+      '#url' => $uri,
+    );
     $row['type'] = String::checkPlain(node_get_type_label($entity));
     $row['author']['data'] = array(
       '#theme' => 'username',
diff --git a/core/modules/node/src/NodeViewBuilder.php b/core/modules/node/src/NodeViewBuilder.php
index 8cf1c04..1136428 100644
--- a/core/modules/node/src/NodeViewBuilder.php
+++ b/core/modules/node/src/NodeViewBuilder.php
@@ -146,7 +146,7 @@ protected static function buildLinks(NodeInterface $entity, $view_mode) {
         'title' => t('Read more<span class="visually-hidden"> about @title</span>', array(
           '@title' => $node_title_stripped,
         )),
-        'href' => 'node/' . $entity->id(),
+        'url' => $entity->urlInfo(),
         'language' => $entity->language(),
         'html' => TRUE,
         'attributes' => array(
diff --git a/core/modules/node/src/Plugin/views/area/ListingEmpty.php b/core/modules/node/src/Plugin/views/area/ListingEmpty.php
index cba41c2..8804f2e 100644
--- a/core/modules/node/src/Plugin/views/area/ListingEmpty.php
+++ b/core/modules/node/src/Plugin/views/area/ListingEmpty.php
@@ -8,6 +8,7 @@
 namespace Drupal\node\Plugin\views\area;
 
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Url;
 use Drupal\views\Plugin\views\area\AreaPluginBase;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -67,7 +68,7 @@ public function render($empty = FALSE) {
         '#theme' => 'links',
         '#links' => array(
           array(
-            'href' => 'node/add',
+            'url' => Url::fromRoute('node.add_page'),
             'title' => $this->t('Add content'),
           ),
         ),
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 2575128..d177860 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -258,7 +258,7 @@ function shortcut_renderable_links($shortcut_set = NULL) {
     $shortcut = \Drupal::entityManager()->getTranslationFromContext($shortcut);
     $links[] = array(
       'title' => $shortcut->label(),
-      'href' => $shortcut->path->value,
+      'url' => \Drupal::service('path.validator')->getUrlIfValid($shortcut->path->value),
     );
     $cache_tags = Cache::mergeTags($cache_tags, $shortcut->getCacheTag());
   }
@@ -378,7 +378,7 @@ function shortcut_toolbar() {
         'tab' => array(
           '#type' => 'link',
           '#title' => t('Shortcuts'),
-          '#href' => 'admin/config/user-interface/shortcut',
+          '#url' => Url::fromRoute('shortcut.set_admin'),
           '#attributes' => array(
             'title' => t('Shortcuts'),
             'class' => array('toolbar-icon', 'toolbar-icon-shortcut'),
diff --git a/core/modules/shortcut/src/Form/SetCustomize.php b/core/modules/shortcut/src/Form/SetCustomize.php
index e778d8c..2d026b5 100644
--- a/core/modules/shortcut/src/Form/SetCustomize.php
+++ b/core/modules/shortcut/src/Form/SetCustomize.php
@@ -64,11 +64,11 @@ public function form(array $form, FormStateInterface $form_state) {
 
       $links['edit'] = array(
         'title' => t('Edit'),
-        'href' => "admin/config/user-interface/shortcut/link/$id",
+        'url' => $shortcut->urlInfo(),
       );
       $links['delete'] = array(
         'title' => t('Delete'),
-        'href' => "admin/config/user-interface/shortcut/link/$id/delete",
+        'url' => $shortcut->urlInfo('delete-form'),
       );
       $form['shortcuts']['links'][$id]['operations'] = array(
         '#type' => 'operations',
diff --git a/core/modules/simpletest/src/Form/SimpletestResultsForm.php b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
index 56d4f59..7d39496 100644
--- a/core/modules/simpletest/src/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/src/Form/SimpletestResultsForm.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\simpletest\TestDiscovery;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -238,7 +239,7 @@ public function buildForm(array $form, FormStateInterface $form_state, $test_id
     $form['action']['return'] = array(
       '#type' => 'link',
       '#title' => $this->t('Return to list'),
-      '#href' => 'admin/config/development/testing',
+      '#url' => Url::fromRoute('simpletest.test_form'),
     );
 
     if (is_numeric($test_id)) {
diff --git a/core/modules/system/src/Controller/DbUpdateController.php b/core/modules/system/src/Controller/DbUpdateController.php
index da7e4c3..2a7992c 100644
--- a/core/modules/system/src/Controller/DbUpdateController.php
+++ b/core/modules/system/src/Controller/DbUpdateController.php
@@ -216,7 +216,8 @@ protected function info() {
       '#type' => 'link',
       '#title' => $this->t('Continue'),
       '#attributes' => array('class' => array('button', 'button--primary')),
-    ) + $url->toRenderArray();
+      '#url' => $url,
+    );
     return $build;
   }
 
@@ -620,12 +621,12 @@ public static function batchFinished($success, $results, $operations) {
   protected function helpfulLinks() {
     $links['front'] = array(
       'title' => $this->t('Front page'),
-      'href' => '<front>',
+      'route_name' => '<front>',
     );
     if ($this->account->hasPermission('access administration pages')) {
       $links['admin-pages'] = array(
         'title' => $this->t('Administration pages'),
-        'href' => 'admin',
+        'route_name' => 'system.admin',
       );
     }
     return $links;
diff --git a/core/modules/system/src/Form/ModulesListForm.php b/core/modules/system/src/Form/ModulesListForm.php
index c1b2b73..b24f8ca 100644
--- a/core/modules/system/src/Form/ModulesListForm.php
+++ b/core/modules/system/src/Form/ModulesListForm.php
@@ -22,6 +22,7 @@
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Routing\RouteProviderInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -258,7 +259,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) {
         $row['links']['help'] = array(
           '#type' => 'link',
           '#title' => $this->t('Help'),
-          '#href' => 'admin/help/' . $module->getName(),
+          '#url' => Url::fromRoute('help.page', ['name' => $module->getName()]),
           '#options' => array('attributes' => array('class' =>  array('module-link', 'module-link-help'), 'title' => $this->t('Help'))),
         );
       }
@@ -270,7 +271,7 @@ protected function buildRow(array $modules, Extension $module, $distribution) {
       $row['links']['permissions'] = array(
         '#type' => 'link',
         '#title' => $this->t('Permissions'),
-        '#href' => 'admin/people/permissions',
+        '#url' => Url::fromRoute('user.admin_permissions'),
         '#options' => array('fragment' => 'module-' . $module->getName(), 'attributes' => array('class' => array('module-link', 'module-link-permissions'), 'title' => $this->t('Configure permissions'))),
       );
     }
diff --git a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
index 9757a2b..73879ee 100644
--- a/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
+++ b/core/modules/system/src/Tests/Common/RenderElementTypesTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Common;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Url;
 use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
@@ -112,7 +113,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag generation without extra classes",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
         ),
         'expected' => '//div[@class="more-link"]/a[@href="http://drupal.org" and text()="More"]',
       ),
@@ -120,7 +121,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag generation with different link text",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#title' => 'More Titles',
         ),
         'expected' => '//div[@class="more-link"]/a[@href="http://drupal.org" and text()="More Titles"]',
@@ -129,7 +130,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag generation with attributes on wrapper",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#theme_wrappers' => array(
             'container' => array(
               '#attributes' => array(
@@ -145,9 +146,9 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag with a relative path",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'a/link',
+          '#route_name' => 'router_test.1',
         ),
-        'expected' => '//div[@class="more-link"]/a[@href="' . _url('a/link') . '" and text()="More"]',
+        'expected' => '//div[@class="more-link"]/a[@href="' . _url('router_test/test1') . '" and text()="More"]',
       ),
       array(
         'name' => "#type 'more_link' anchor tag with a route",
@@ -162,7 +163,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag with an absolute path",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => 'admin/content',
+          '#route_name' => 'system.admin_content',
           '#options' => array('absolute' => TRUE),
         ),
         'expected' => '//div[@class="more-link"]/a[@href="' . _url('admin/content', array('absolute' => TRUE)) . '" and text()="More"]',
@@ -171,7 +172,7 @@ function testMoreLink() {
         'name' => "#type 'more_link' anchor tag to the front page",
         'value' => array(
           '#type' => 'more_link',
-          '#href' => '<front>',
+          '#route_name' => '<front>',
         ),
         'expected' => '//div[@class="more-link"]/a[@href="' . _url('<front>') . '" and text()="More"]',
       ),
diff --git a/core/modules/system/src/Tests/Common/RenderTest.php b/core/modules/system/src/Tests/Common/RenderTest.php
index d07085c..033845f 100644
--- a/core/modules/system/src/Tests/Common/RenderTest.php
+++ b/core/modules/system/src/Tests/Common/RenderTest.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Serialization\Json;
 use Drupal\Component\Utility\Html;
 use Drupal\Core\Render\Element;
+use Drupal\Core\Url;
 use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
@@ -120,7 +121,7 @@ function testDrupalRenderBasics() {
             ),
           ),
           '#attributes' => array('id' => 'foo'),
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#title' => 'bar',
         ),
         'expected' => '<div class="baz"><a href="http://drupal.org" id="foo">bar</a></div>' . "\n",
@@ -131,7 +132,7 @@ function testDrupalRenderBasics() {
         'name' => '#theme_wrappers attribute disambiguation with undefined #theme attribute',
         'value' => array(
           '#type' => 'link',
-          '#href' => 'http://drupal.org',
+          '#url' => Url::fromUri('http://drupal.org'),
           '#title' => 'foo',
           '#theme_wrappers' => array(
             'container' => array(
diff --git a/core/modules/system/src/Tests/Common/RenderWebTest.php b/core/modules/system/src/Tests/Common/RenderWebTest.php
index 3e58fb0..52dca2d 100644
--- a/core/modules/system/src/Tests/Common/RenderWebTest.php
+++ b/core/modules/system/src/Tests/Common/RenderWebTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\system\Tests\Common;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -105,13 +106,13 @@ function testDrupalRenderFormElements() {
     $element = array(
       '#type' => 'link',
       '#title' => $this->randomMachineName(),
-      '#href' => $this->randomMachineName(),
+      '#url' => Url::fromUri($this->randomMachineName()),
       '#options' => array(
         'absolute' => TRUE,
       ),
     );
     $this->assertRenderedElement($element, '//a[@href=:href and contains(., :title)]', array(
-      ':href' => _url($element['#href'], array('absolute' => TRUE)),
+      ':href' => $element['#url']->toString(),
       ':title' => $element['#title'],
     ));
 
diff --git a/core/modules/system/src/Tests/Common/UrlTest.php b/core/modules/system/src/Tests/Common/UrlTest.php
index 0097512..b719ebf 100644
--- a/core/modules/system/src/Tests/Common/UrlTest.php
+++ b/core/modules/system/src/Tests/Common/UrlTest.php
@@ -9,6 +9,7 @@
 
 use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Url;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -38,15 +39,10 @@ function testLinkXSS() {
     $sanitized_path = check_url(_url($path));
     $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by _l().', array('@path' => $path)));
 
-    // Test #type 'link'.
-    $link_array =  array(
-      '#type' => 'link',
-      '#title' => $this->randomMachineName(),
-      '#href' => $path,
-    );
-    $type_link = drupal_render($link_array);
+    // Test _url().
+    $link = _url($path);
     $sanitized_path = check_url(_url($path));
-    $this->assertTrue(strpos($type_link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', array('@path' => $path)));
+    $this->assertTrue(strpos($link, $sanitized_path) !== FALSE, format_string('XSS attack @path was filtered by #theme', array('@path' => $path)));
   }
 
   /**
@@ -60,7 +56,7 @@ function testLinkAttributes() {
       '#options' => array(
         'language' => $language,
       ),
-      '#href' => 'http://drupal.org',
+      '#url' => Url::fromUri('http://drupal.org'),
       '#title' => 'bar',
     );
     $langcode = $language->id;
@@ -122,7 +118,7 @@ function testLinkAttributes() {
     $type_link = array(
       '#type' => 'link',
       '#title' => $this->randomMachineName(),
-      '#href' => current_path(),
+      '#route_name' => '<current>',
       '#options' => array(
         'attributes' => array(
           'class' => array($class_theme),
@@ -149,7 +145,7 @@ function testLinkRenderArrayText() {
     $type_link_plain_array = array(
       '#type' => 'link',
       '#title' => 'foo',
-      '#href' => 'http://drupal.org',
+      '#url' => Url::fromUri('http://drupal.org'),
     );
     $type_link_plain = drupal_render($type_link_plain_array);
     $this->assertEqual($type_link_plain, $l);
@@ -158,7 +154,7 @@ function testLinkRenderArrayText() {
     $type_link_nested_array = array(
       '#type' => 'link',
       '#title' => array('#markup' => 'foo'),
-      '#href' => 'http://drupal.org',
+      '#url' => Url::fromUri('http://drupal.org'),
     );
     $type_link_nested = drupal_render($type_link_nested_array);
     $this->assertEqual($type_link_nested, $l);
diff --git a/core/modules/system/src/Tests/Theme/FunctionsTest.php b/core/modules/system/src/Tests/Theme/FunctionsTest.php
index c8d068c..52533ab 100644
--- a/core/modules/system/src/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/src/Tests/Theme/FunctionsTest.php
@@ -190,14 +190,14 @@ function testLinks() {
     $variables['links'] = array(
       'a link' => array(
         'title' => 'A <link>',
-        'href' => 'a/link',
+        'href' => 'base://a/link',
       ),
       'plain text' => array(
         'title' => 'Plain "text"',
       ),
       'front page' => array(
         'title' => 'Front page',
-        'href' => '<front>',
+        'route_name' => '<front>',
       ),
       'router-test' => array(
         'title' => 'Test route',
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index f721626..c872f4b 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -412,7 +412,7 @@ function hook_menu_local_tasks(&$data, $route_name) {
     '#theme' => 'menu_local_action',
     '#link' => array(
       'title' => t('Add content'),
-      'href' => 'node/add',
+      'route_name' => 'node.add_page',
       'localized_options' => array(
         'attributes' => array(
           'title' => t('Add content'),
@@ -426,7 +426,7 @@ function hook_menu_local_tasks(&$data, $route_name) {
     '#theme' => 'menu_local_task',
     '#link' => array(
       'title' => t('Example tab'),
-      'href' => 'node/add',
+      'route_name' => 'node.add_page',
       'localized_options' => array(
         'attributes' => array(
           'title' => t('Add content'),
@@ -2277,7 +2277,7 @@ function hook_system_themes_page_alter(&$theme_groups) {
       // Add a foo link to each list of theme operations.
       $theme->operations[] = array(
         'title' => t('Foo'),
-        'href' => 'admin/appearance/foo',
+        'route_name' => 'system.themes_page',
         'query' => array('theme' => $theme->getName())
       );
     }
diff --git a/core/modules/system/tests/modules/ajax_test/ajax_test.module b/core/modules/system/tests/modules/ajax_test/ajax_test.module
index 6127b2e..870a157 100644
--- a/core/modules/system/tests/modules/ajax_test/ajax_test.module
+++ b/core/modules/system/tests/modules/ajax_test/ajax_test.module
@@ -11,6 +11,7 @@
 use Drupal\Core\Ajax\OpenModalDialogCommand;
 use Drupal\Core\Ajax\CloseDialogCommand;
 use Drupal\Core\Ajax\HtmlCommand;
+use Drupal\Core\Url;
 
 /**
  * Menu callback: Returns an element suitable for use by
@@ -108,7 +109,7 @@ function ajax_test_dialog_contents() {
     'cancel' => array(
       '#type' => 'link',
       '#title' => 'Cancel',
-      '#href' => '',
+      '#url' => Url::fromRoute('<front>'),
       '#attributes' => array(
         // This is a special class to which JavaScript assigns dialog closing
         // behavior.
diff --git a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
index ac0399f..ab25fde 100644
--- a/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
+++ b/core/modules/system/tests/modules/ajax_test/src/Controller/AjaxTestController.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\ajax_test\Controller;
 
+use Drupal\Core\Url;
+
 /**
  * Provides content for dialog tests.
  */
@@ -56,7 +58,7 @@ public function dialog() {
     $build['link'] = array(
       '#type' => 'link',
       '#title' => 'Link 1 (modal)',
-      '#href' => 'ajax-test/dialog-contents',
+      '#url' => Url::fromRoute('ajax_test.dialog_contents'),
       '#attributes' => array(
         'class' => array('use-ajax'),
         'data-accepts' => 'application/vnd.drupal-modal',
@@ -69,7 +71,7 @@ public function dialog() {
       '#links' => array(
         'link2' => array(
           'title' => 'Link 2 (modal)',
-          'href' => 'ajax-test/dialog-contents',
+          'url' => Url::fromRoute('ajax_test.dialog_contents'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-modal',
@@ -80,7 +82,7 @@ public function dialog() {
         ),
         'link3' => array(
           'title' => 'Link 3 (non-modal)',
-          'href' => 'ajax-test/dialog-contents',
+          'url' => Url::fromRoute('ajax_test.dialog_contents'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-dialog',
@@ -92,14 +94,14 @@ public function dialog() {
         ),
         'link4' => array(
           'title' => 'Link 4 (close non-modal if open)',
-          'href' => 'ajax-test/dialog-close',
+          'url' => Url::fromRoute('ajax_test.dialog_close'),
           'attributes' => array(
             'class' => array('use-ajax'),
           ),
         ),
         'link5' => array(
           'title' => 'Link 5 (form)',
-          'href' => 'ajax-test/dialog-form',
+          'url' => Url::fromRoute('ajax_test.dialog_form'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-modal',
@@ -107,7 +109,7 @@ public function dialog() {
         ),
         'link6' => array(
           'title' => 'Link 6 (entity form)',
-          'href' => 'admin/structure/contact/add',
+          'url' => Url::fromRoute('contact.form_add'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-modal',
@@ -119,7 +121,7 @@ public function dialog() {
         ),
         'link7' => array(
           'title' => 'Link 7 (non-modal, no target)',
-          'href' => 'ajax-test/dialog-contents',
+          'url' => Url::fromRoute('ajax_test.dialog_contents'),
           'attributes' => array(
             'class' => array('use-ajax'),
             'data-accepts' => 'application/vnd.drupal-dialog',
diff --git a/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php b/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
index be4ce96..cf82dba 100644
--- a/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
+++ b/core/modules/system/tests/modules/common_test/src/Controller/CommonTestController.php
@@ -8,6 +8,7 @@
 namespace Drupal\common_test\Controller;
 
 use Drupal\Component\Utility\String;
+use Drupal\Core\Url;
 use Symfony\Component\HttpFoundation\Response;
 
 /**
@@ -25,7 +26,7 @@ public function typeLinkActiveClass() {
       'no_query' => array(
         '#type' => 'link',
         '#title' => t('Link with no query string'),
-        '#href' => current_path(),
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'set_active_class' => TRUE,
         ),
@@ -33,7 +34,7 @@ public function typeLinkActiveClass() {
       'with_query' => array(
         '#type' => 'link',
         '#title' => t('Link with a query string'),
-        '#href' => current_path(),
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'query' => array(
             'foo' => 'bar',
@@ -45,7 +46,7 @@ public function typeLinkActiveClass() {
       'with_query_reversed' => array(
         '#type' => 'link',
         '#title' => t('Link with the same query string in reverse order'),
-        '#href' => current_path(),
+        '#url' => Url::fromRoute('<current>'),
         '#options' => array(
           'query' => array(
             'one' => 'two',
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index e17eb01..e158350 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -14,6 +14,7 @@
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Entity\Entity\EntityFormDisplay;
+use Drupal\Core\Url;
 
 /**
  * Filter that limits test entity list to revisable ones.
@@ -384,11 +385,19 @@ function entity_test_entity_predelete(EntityInterface $entity) {
  * Implements hook_entity_operation_alter().
  */
 function entity_test_entity_operation_alter(array &$operations, EntityInterface $entity) {
-  $operations['test_operation'] = array(
-    'title' => format_string('Test Operation: @label', array('@label' => $entity->label())),
-    'href' => $entity->url() . '/test_operation',
-    'weight' => 50,
-  );
+  $valid_entity_type_ids = [
+    'user_role',
+    'block',
+  ];
+  if (in_array($entity->getEntityTypeId(), $valid_entity_type_ids)) {
+    if (\Drupal::service('router.route_provider')->getRouteByName("entity.{$entity->getEntityTypeId()}.test_operation")) {
+      $operations['test_operation'] = array(
+        'title' => format_string('Test Operation: @label', array('@label' => $entity->label())),
+        'url' => Url::fromRoute("entity.{$entity->getEntityTypeId()}.test_operation", [$entity->getEntityTypeId() => $entity->id()]),
+        'weight' => 50,
+      );
+    }
+  }
 }
 
 /**
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
index e3d0adc..34cbc56 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
+++ b/core/modules/system/tests/modules/entity_test/entity_test.routing.yml
@@ -76,6 +76,19 @@ entity.entity_test.list_empty:
   requirements:
     _access: 'TRUE'
 
+entity.block.test_operation:
+  path: '/admin/structure/block/manage/{block}/test_operation'
+  defaults:
+    _entity_view: 'entity_test'
+  requirements:
+    _access: 'TRUE'
+
+entity.user_role.test_operation:
+  path: '/admin/people/roles/manage/{user_role}/test_operation'
+  defaults:
+    _entity_view: 'entity_test'
+  requirements:
+    _access: 'TRUE'
 
 route_callbacks:
   - '\Drupal\entity_test\Routing\EntityTestRoutes::routes'
diff --git a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
index a02be18..7d2cb79 100644
--- a/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
+++ b/core/modules/system/tests/modules/theme_test/src/EventSubscriber/ThemeTestSubscriber.php
@@ -44,7 +44,7 @@ public function onRequest(GetResponseEvent $event) {
       // returning output and theming the page as a whole.
       $more_link = array(
         '#type' => 'more_link',
-        '#href' => 'user',
+        '#route_name' => 'user.page',
         '#attributes' => array('title' => 'Themed output generated in a KernelEvents::REQUEST listener'),
       );
       $GLOBALS['theme_test_output'] = drupal_render($more_link);
diff --git a/core/modules/taxonomy/src/Form/OverviewTerms.php b/core/modules/taxonomy/src/Form/OverviewTerms.php
index 95e6178..ffe0ea1 100644
--- a/core/modules/taxonomy/src/Form/OverviewTerms.php
+++ b/core/modules/taxonomy/src/Form/OverviewTerms.php
@@ -225,7 +225,8 @@ public function buildForm(array $form, FormStateInterface $form_state, Vocabular
         '#prefix' => !empty($indentation) ? drupal_render($indentation) : '',
         '#type' => 'link',
         '#title' => $term->getName(),
-      ) + $term->urlInfo()->toRenderArray();
+        '#url' => $term->urlInfo(),
+      );
       if ($taxonomy_vocabulary->hierarchy != TAXONOMY_HIERARCHY_MULTIPLE && count($tree) > 1) {
         $parent_fields = TRUE;
         $form['terms'][$key]['term']['tid'] = array(
diff --git a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index 856b73e..00b2e8f 100644
--- a/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/taxonomy/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -42,11 +42,14 @@ public function viewElements(FieldItemListInterface $items) {
         $elements[$delta] = array(
           '#type' => 'link',
           '#title' => $term->getName(),
-        ) + $term->urlInfo()->toRenderArray();
+          '#url' => $term->urlInfo(),
+        );
 
         if (!empty($item->_attributes)) {
-          $elements[$delta]['#options'] += array('attributes' => array());
-          $elements[$delta]['#options']['attributes'] += $item->_attributes;
+          $options = $elements[$delta]['#url']->getOptions();
+          $options += array('attributes' => array());
+          $options['attributes'] += $item->_attributes;
+          $elements[$delta]['#url']->setOptions($options);
           // Unset field item attributes since they have been included in the
           // formatter output and should not be rendered in the field template.
           unset($item->_attributes);
diff --git a/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php b/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php
index ef45d4e..5d83b7c 100644
--- a/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php
+++ b/core/modules/telephone/src/Plugin/Field/FieldFormatter/TelephoneLinkFormatter.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Field\FormatterBase;
 use Drupal\Core\Field\FieldItemListInterface;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 
 /**
  * Plugin implementation of the 'telephone_link' formatter.
@@ -78,7 +79,7 @@ public function viewElements(FieldItemListInterface $items) {
         // itself as title.
         '#title' => $title_setting ?: $item->value,
         // Prepend 'tel:' to the telephone number.
-        '#href' => 'tel:' . rawurlencode(preg_replace('/\s+/', '', $item->value)),
+        '#url' => Url::fromUri('tel:' . rawurlencode(preg_replace('/\s+/', '', $item->value))),
         '#options' => array('external' => TRUE),
       );
 
diff --git a/core/modules/toolbar/src/Element/ToolbarItem.php b/core/modules/toolbar/src/Element/ToolbarItem.php
index 18ade46..e1249b9 100644
--- a/core/modules/toolbar/src/Element/ToolbarItem.php
+++ b/core/modules/toolbar/src/Element/ToolbarItem.php
@@ -33,7 +33,7 @@ public function getInfo() {
       'tab' => array(
         '#type' => 'link',
         '#title' => NULL,
-        '#href' => '',
+        '#route_name' => '<front>',
       ),
     );
   }
diff --git a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
index 69dac7a..deda1d1 100644
--- a/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
+++ b/core/modules/toolbar/tests/modules/toolbar_test/toolbar_test.module
@@ -17,7 +17,7 @@ function toolbar_test_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Test tab'),
-      '#href' => '',
+      '#route_name' => '<front>',
       '#options' => array(
         'html' => FALSE,
         'attributes' => array(
diff --git a/core/modules/toolbar/toolbar.api.php b/core/modules/toolbar/toolbar.api.php
index ab9b75a..bce69ae 100644
--- a/core/modules/toolbar/toolbar.api.php
+++ b/core/modules/toolbar/toolbar.api.php
@@ -4,6 +4,7 @@
  * @file
  * Hooks provided by the toolbar module.
  */
+use Drupal\Core\Url;
 
 /**
  * @addtogroup hooks
@@ -72,7 +73,7 @@ function hook_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Home'),
-      '#href' => '<front>',
+      '#url' => Url::fromRoute('<front>'),
       '#options' => array(
         'attributes' => array(
           'title' => t('Home page'),
@@ -96,7 +97,7 @@ function hook_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Shopping cart'),
-      '#href' => '/cart',
+      '#url' => Url::fromRoute('cart'),
       '#options' => array(
         'html' => FALSE,
         'attributes' => array(
@@ -131,7 +132,7 @@ function hook_toolbar() {
       '#theme' => 'user_message_toolbar_tab',
       '#theme_wrappers' => array(),
       '#title' => t('Messages'),
-      '#href' => '/user/messages',
+      '#url' => Url::fromRoute('user.message'),
       '#options' => array(
         'attributes' => array(
           'title' => t('Messages'),
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index 2fbf64d..d7121d5 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -14,6 +14,7 @@
 use Drupal\Component\Utility\Crypt;
 use Drupal\Component\Utility\String;
 use Drupal\user\Entity\Role;
+use Drupal\Core\Url;
 
 /**
  * Implements hook_help().
@@ -94,7 +95,7 @@ function toolbar_element_info() {
     'tab' => array(
       '#type' => 'link',
       '#title' => NULL,
-      '#href' => '',
+      '#url' => Url::fromRoute('<front>'),
     ),
   );
   return $elements;
@@ -179,7 +180,7 @@ function toolbar_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Back to site'),
-      '#href' => '<front>',
+      '#url' => Url::fromRoute('<front>'),
       '#attributes' => array(
         'title' => t('Return to site content'),
         'class' => array('toolbar-icon', 'toolbar-icon-escape-admin'),
@@ -218,7 +219,7 @@ function toolbar_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => t('Manage'),
-      '#href' => 'admin',
+      '#url' => Url::fromRoute('system.admin'),
       '#attributes' => array(
         'title' => t('Admin menu'),
         'class' => array('toolbar-icon', 'toolbar-icon-menu'),
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 82aca31..a5c99e1 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1450,7 +1450,7 @@ function user_toolbar() {
     $links = array(
       'account' => array(
         'title' => t('View profile'),
-        'href' => 'user',
+        'url' => Url::fromRoute('user.page'),
         'html' => TRUE,
         'attributes' => array(
           'title' => t('User account'),
@@ -1458,7 +1458,7 @@ function user_toolbar() {
       ),
       'account_edit' => array(
         'title' => t('Edit profile'),
-        'href' => 'user/' . $user->id() . '/edit',
+        'url' => Url::fromRoute('entity.user.edit_form', ['user' => $user->id()]),
         'html' => TRUE,
         'attributes' => array(
           'title' => t('Edit user account'),
@@ -1466,7 +1466,7 @@ function user_toolbar() {
       ),
       'logout' => array(
         'title' => t('Log out'),
-        'href' => 'user/logout',
+        'url' => Url::fromRoute('user.logout'),
       ),
     );
   }
@@ -1474,7 +1474,7 @@ function user_toolbar() {
      $links = array(
       'login' => array(
         'title' => t('Log in'),
-        'href' => 'user',
+        'url' => Url::fromRoute('user.page'),
       ),
     );
   }
@@ -1484,7 +1484,7 @@ function user_toolbar() {
     'tab' => array(
       '#type' => 'link',
       '#title' => $user->getUsername(),
-      '#href' => 'user',
+      '#url' => Url::fromRoute('user.page'),
       '#attributes' => array(
         'title' => t('My account'),
         'class' => array('toolbar-icon', 'toolbar-icon-user'),
diff --git a/core/modules/views/src/Plugin/views/field/Links.php b/core/modules/views/src/Plugin/views/field/Links.php
index 7f699f7..dc638ff 100644
--- a/core/modules/views/src/Plugin/views/field/Links.php
+++ b/core/modules/views/src/Plugin/views/field/Links.php
@@ -79,7 +79,7 @@ protected function getLinks() {
       $path = strip_tags(decode_entities(strtr($path, $tokens)));
 
       $links[$field] = array(
-        'href' => $path,
+        'url' => \Drupal::service('path.validator')->getUrlIfValid($path),
         'title' => $title,
       );
       if (!empty($this->options['destination'])) {
diff --git a/core/modules/views/views.theme.inc b/core/modules/views/views.theme.inc
index 9ab4be0..5bd6e90 100644
--- a/core/modules/views/views.theme.inc
+++ b/core/modules/views/views.theme.inc
@@ -1045,7 +1045,7 @@ function template_preprocess_views_mini_pager(&$variables) {
     $li_previous = array(
       '#type' => 'link',
       '#title' => $tags[1],
-      '#href' => $current_path,
+      '#route_name' => '<current>',
       '#options' => array(
         'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] - 1),
         'attributes' => array(
@@ -1068,7 +1068,7 @@ function template_preprocess_views_mini_pager(&$variables) {
     $li_next = array(
       '#type' => 'link',
       '#title' => $tags[3],
-      '#href' => $current_path,
+      '#route_name' => '<current>',
       '#options' => array(
         'query' => pager_query_add_page($parameters, $element, $pager_page_array[$element] + 1),
         'attributes' => array(
diff --git a/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php b/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
index c6e24db..823eabd 100644
--- a/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
+++ b/core/modules/views_ui/src/Form/Ajax/ReorderDisplays.php
@@ -8,6 +8,7 @@
 namespace Drupal\views_ui\Form\Ajax;
 
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 use Drupal\views_ui\ViewUI;
 
 /**
@@ -120,7 +121,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
         'link' => array(
           '#type' => 'link',
           '#title' => '<span>' . $this->t('Remove') . '</span>',
-          '#href' => 'javascript:void()',
+          '#url' => Url::fromUri('javascript:void()'),
           '#options' => array(
             'html' => TRUE,
           ),
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index e621513..2ca0dca 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -427,7 +427,7 @@ public function getDisplayDetails($view, $display) {
               '#type' => 'link',
               '#title' => $this->t('View !display_title', array('!display_title' => $display_title)),
               '#options' => array('alt' => array($this->t("Go to the real page for this display"))),
-              '#href' => $path,
+              '#url' => Url::fromUri("base://$path"),
               '#prefix' => '<li class="view">',
               "#suffix" => '</li>',
             );
@@ -695,12 +695,12 @@ public function renderDisplayTop(ViewUI $view) {
       '#links' => array(
         'edit-details' => array(
           'title' => $this->t('Edit view name/description'),
-          'href' => "admin/structure/views/nojs/edit-details/{$view->id()}/$display_id",
+          'url' => Url::fromRoute('views_ui.form_edit_details', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display_id]),
           'attributes' => array('class' => array('views-ajax-link')),
         ),
         'analyze' => array(
           'title' => $this->t('Analyze view'),
-          'href' => "admin/structure/views/nojs/analyze/{$view->id()}/$display_id",
+          'url' => Url::fromRoute('views_ui.form_analyze', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display_id]),
           'attributes' => array('class' => array('views-ajax-link')),
         ),
         'duplicate' => array(
@@ -708,7 +708,7 @@ public function renderDisplayTop(ViewUI $view) {
         ) + $view->urlInfo('duplicate-form')->toArray(),
         'reorder' => array(
           'title' => $this->t('Reorder displays'),
-          'href' => "admin/structure/views/nojs/reorder-displays/{$view->id()}/$display_id",
+          'url' => Url::fromRoute('views_ui.form_reorder_displays', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display_id]),
           'attributes' => array('class' => array('views-ajax-link')),
         ),
       ),
@@ -734,7 +734,7 @@ public function renderDisplayTop(ViewUI $view) {
       else {
         $element['extra_actions']['#links']['delete'] = array(
           'title' => $this->t('Delete view'),
-          'href' => "admin/structure/views/view/{$view->id()}/delete",
+          'url' => $view->urlInfo('delete-form'),
         );
       }
     }
@@ -944,7 +944,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
 
     $build['#name'] = $build['#title'] = $types[$type]['title'];
 
-    $rearrange_url = "admin/structure/views/nojs/rearrange/{$view->id()}/{$display['id']}/$type";
+    $rearrange_url = Url::fromRoute('views_ui.form_rearrange', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display['id'], 'type' => $type]);
     $class = 'icon compact rearrange';
 
     // Different types now have different rearrange forms, so we use this switch
@@ -953,7 +953,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
       case 'filter':
         // The rearrange form for filters contains the and/or UI, so override
         // the used path.
-        $rearrange_url = "admin/structure/views/nojs/rearrange-filter/{$view->id()}/{$display['id']}";
+        $rearrange_url = Url::fromRoute('views_ui.form_rearrange_filter', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display['id']]);
         // TODO: Add another class to have another symbol for filter rearrange.
         $class = 'icon compact rearrange';
         break;
@@ -993,7 +993,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
 
     $actions['add'] = array(
       'title' => $add_text,
-      'href' => "admin/structure/views/nojs/add-handler/{$view->id()}/{$display['id']}/$type",
+      'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->id(), 'display_id' => $display['id'], 'type' => $type]),
       'attributes' => array('class' => array('icon compact add', 'views-ajax-link'), 'id' => 'views-add-' . $type),
       'html' => TRUE,
     );
@@ -1003,7 +1003,7 @@ public function getFormBucket(ViewUI $view, $type, $display) {
 
       $actions['rearrange'] = array(
         'title' => $rearrange_text,
-        'href' => $rearrange_url,
+        'url' => $rearrange_url,
         'attributes' => array('class' => array($class, 'views-ajax-link'), 'id' => 'views-rearrange-' . $type),
         'html' => TRUE,
       );
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index 3960294..ca61352 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
 use Drupal\views\Views;
 use Drupal\views\ViewExecutable;
 use Drupal\views\ViewStorageInterface;
@@ -180,13 +181,13 @@ function views_ui_view_preview_section_handler_links(ViewExecutable $view, $type
     $field_name = $handler->adminLabel(TRUE);
     $links[$type . '-edit-' . $id] = array(
       'title' => t('Edit @section', array('@section' => $field_name)),
-      'href' => "admin/structure/views/nojs/handler/{$view->storage->id()}/{$display['id']}/$type/$id",
+      'url' => Url::fromRoute('views_ui.form_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type, 'id' => $id]),
       'attributes' => array('class' => array('views-ajax-link')),
     );
   }
   $links[$type . '-add'] = array(
     'title' => t('Add new'),
-    'href' => "admin/structure/views/nojs/add-handler/{$view->storage->id()}/{$display['id']}/$type",
+    'url' => Url::fromRoute('views_ui.form_add_handler', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
     'attributes' => array('class' => array('views-ajax-link')),
   );
 
@@ -201,7 +202,7 @@ function views_ui_view_preview_section_display_category_links(ViewExecutable $vi
   $links = array(
     $type . '-edit' => array(
       'title' => t('Edit @section', array('@section' => $title)),
-      'href' => "admin/structure/views/nojs/display/{$view->storage->id()}/{$display['id']}/$type",
+      'url' => Url::fromRoute('views_ui.form_display', ['js' => 'nojs', 'view' => $view->storage->id(), 'display_id' => $display['id'], 'type' => $type]),
       'attributes' => array('class' => array('views-ajax-link')),
     ),
   );
diff --git a/core/modules/views_ui/views_ui.theme.inc b/core/modules/views_ui/views_ui.theme.inc
index 80f2f45..4528490 100644
--- a/core/modules/views_ui/views_ui.theme.inc
+++ b/core/modules/views_ui/views_ui.theme.inc
@@ -9,6 +9,7 @@
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Template\Attribute;
+use Drupal\Core\Url;
 
 /**
  * Prepares variables for Views UI display tab setting templates.
@@ -283,7 +284,7 @@ function template_preprocess_views_ui_rearrange_filter_form(&$variables) {
 
         $remove_link = array(
           '#type' => 'link',
-          '#href' => '',
+          '#url' => Url::fromRoute('<none>'),
           '#title' => '<span>' . t('Remove') . '</span>',
           '#weight' => '1',
           '#options' => array(
diff --git a/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php b/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
index 176d5b4..842304c 100644
--- a/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/ConfirmFormHelperTest.php
@@ -47,7 +47,7 @@ public function testCancelLinkRoute() {
       ->method('getCancelUrl')
       ->will($this->returnValue($cancel_route));
     $link = ConfirmFormHelper::buildCancelLink($form, new Request());
-    $this->assertSame($route_name, $link['#route_name']);
+    $this->assertEquals(Url::fromRoute($route_name), $link['#url']);
   }
 
   /**
@@ -65,14 +65,13 @@ public function testCancelLinkRouteWithParams() {
         'absolute' => TRUE,
       ),
     );
+    $expected = Url::fromRoute($cancel_route['route_name'], $cancel_route['route_parameters'], $cancel_route['options']);
     $form = $this->getMock('Drupal\Core\Form\ConfirmFormInterface');
     $form->expects($this->any())
       ->method('getCancelUrl')
-      ->will($this->returnValue(new Url($cancel_route['route_name'], $cancel_route['route_parameters'], $cancel_route['options'])));
+      ->will($this->returnValue($expected));
     $link = ConfirmFormHelper::buildCancelLink($form, new Request());
-    $this->assertSame($cancel_route['route_name'], $link['#route_name']);
-    $this->assertSame($cancel_route['route_parameters'], $link['#route_parameters']);
-    $this->assertSame($cancel_route['options'], $link['#options']);
+    $this->assertEquals($expected, $link['#url']);
   }
 
   /**
@@ -94,9 +93,7 @@ public function testCancelLinkRouteWithUrl() {
       ->method('getCancelUrl')
       ->will($this->returnValue($cancel_route));
     $link = ConfirmFormHelper::buildCancelLink($form, new Request());
-    $this->assertSame($cancel_route->getRouteName(), $link['#route_name']);
-    $this->assertSame($cancel_route->getRouteParameters(), $link['#route_parameters']);
-    $this->assertSame($cancel_route->getOptions(), $link['#options']);
+    $this->assertSame($cancel_route, $link['#url']);
   }
 
   /**
@@ -108,7 +105,7 @@ public function testCancelLinkDestination() {
     $query = array('destination' => 'baz');
     $form = $this->getMock('Drupal\Core\Form\ConfirmFormInterface');
     $link = ConfirmFormHelper::buildCancelLink($form, new Request($query));
-    $this->assertSame($query['destination'], $link['#href']);
+    $this->assertSame('base://' . $query['destination'], $link['#url']->getUri());
   }
 
 }
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index cf0496f..75c4768 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -350,9 +350,7 @@ public function testAccess($access) {
    */
   public function testRenderAccess($access) {
     $element = array(
-      '#route_name' => 'entity.node.canonical',
-      '#route_parameters' => ['node' => 3],
-      '#options' => [],
+      '#url' => Url::fromRoute('entity.node.canonical', ['node' => 3]),
     );
     $this->container->set('current_user', $this->getMock('Drupal\Core\Session\AccountInterface'));
     $this->container->set('access_manager', $this->getMockAccessManager($access));
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 5721010..d55fc9d 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -11,6 +11,8 @@
 use Drupal\Core\Url;
 use Drupal\Core\Utility\LinkGenerator;
 use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * @coversDefaultClass \Drupal\Core\Utility\LinkGenerator
@@ -66,7 +68,15 @@ protected function setUp() {
     $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGenerator', array(), array(), '', FALSE);
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
 
-    $this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler);
+    $request_stack = new RequestStack();
+    $request = Request::create('/some/path');
+    $request_stack->push($request);
+    $path_processor_manager = $this->getMock('Drupal\Core\PathProcessor\OutboundPathProcessorInterface');
+    $path_processor_manager->expects($this->any())
+      ->method('processOutbound')
+      ->willReturnArgument(0);
+
+    $this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler, $path_processor_manager, $request_stack);
     $this->urlAssembler = $this->getMock('\Drupal\Core\Utility\UnroutedUrlAssemblerInterface');
   }
 
