diff --git a/core/core.services.yml b/core/core.services.yml
index 91a2c2b..7abab67 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -489,6 +489,9 @@ services:
     arguments: ['@router.route_provider', '@path_processor_manager', '@route_processor_manager', '@config.factory', '@settings', '@logger.channel.default', '@request_stack']
     calls:
       - [setContext, ['@?router.request_context']]
+  unrouted_url_assembler:
+    class: Drupal\Core\Utility\UnroutedUrlAssembler
+    arguments: ['@request_stack', '@config.factory' ]
   link_generator:
     class: Drupal\Core\Utility\LinkGenerator
     arguments: ['@url_generator', '@module_handler']
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 7e6e12f..e36cab7 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -452,11 +452,17 @@ function _batch_finished() {
     if ($_batch['form_state']->getRedirect() === NULL) {
       $redirect = $_batch['batch_redirect'] ?: $_batch['source_url'];
       $options = UrlHelper::parse($redirect);
-      if (!UrlHelper::isExternal($options['path'])) {
-        $options['path'] = $GLOBALS['base_url'] . '/' . $options['path'];
+      if (UrlHelper::isExternal($options['path'])) {
+        $redirect = Url::unrouted($options['path'], $options);
+      }
+      else {
+        $redirect = \Drupal::pathValidator()->getUrlIfValid($options['path']);
+        if (!$redirect) {
+          // Stay on the same page if the redirect was invalid.
+          $redirect = Url::routed('<current>');
+        }
+        $redirect->setOptions($options);
       }
-      $redirect = Url::createFromPath($options['path']);
-      $redirect->setOptions($options);
       $_batch['form_state']->setRedirectUrl($redirect);
     }
 
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 1379b4e..f46c9a9 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -639,6 +639,9 @@ function _format_date_callback(array $matches = NULL, $new_langcode = NULL) {
  * alternative than url().
  *
  * @see \Drupal\Core\Routing\UrlGeneratorInterface::generateFromPath().
+ *
+ * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
+ *   System paths should not be used - use route names and parameters.
  */
 function url($path = NULL, array $options = array()) {
   return \Drupal::urlGenerator()->generateFromPath($path, $options);
diff --git a/core/lib/Drupal/Core/Menu/MenuLinkBase.php b/core/lib/Drupal/Core/Menu/MenuLinkBase.php
index b09bfa9..6edbcc0 100644
--- a/core/lib/Drupal/Core/Menu/MenuLinkBase.php
+++ b/core/lib/Drupal/Core/Menu/MenuLinkBase.php
@@ -141,8 +141,7 @@ public function getUrlObject($title_attribute = TRUE) {
       return new Url($this->pluginDefinition['route_name'], $this->pluginDefinition['route_parameters'], $options);
     }
     else {
-      $url = Url::createFromPath($this->pluginDefinition['url']);
-      $url->setOptions($options);
+      $url = Url::unrouted($this->pluginDefinition['url'], $options);
       return $url;
     }
   }
diff --git a/core/lib/Drupal/Core/Path/PathValidator.php b/core/lib/Drupal/Core/Path/PathValidator.php
index 1b519c7..d7506c6 100644
--- a/core/lib/Drupal/Core/Path/PathValidator.php
+++ b/core/lib/Drupal/Core/Path/PathValidator.php
@@ -99,7 +99,7 @@ public function getUrlIfValid($path) {
       if (empty($parsed_url['path'])) {
         return FALSE;
       }
-      return Url::createFromPath($path);
+      return Url::unrouted($path);
     }
 
     $path = ltrim($path, '/');
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index d97f017..4ee3692 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -7,10 +7,10 @@
 
 namespace Drupal\Core;
 
-use Drupal\Component\Utility\UrlHelper;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Utility\UnroutedUrlAssemblerInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -28,6 +28,13 @@ class Url {
   protected $urlGenerator;
 
   /**
+   * The URL builder.
+   *
+   * @var \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+   */
+  protected $urlAssember;
+
+  /**
    * The access manager
    *
    * @var \Drupal\Core\Access\AccessManagerInterface
@@ -56,20 +63,27 @@ class Url {
   protected $options = array();
 
   /**
-   * Indicates whether this URL is external.
+   * Indicates whether this object contains an external URL.
    *
    * @var bool
    */
   protected $external = FALSE;
 
   /**
-   * The external path.
+   * Indicates whether this URL is a URI instead of a route.
+   *
+   * @var bool
+   */
+  protected $unrouted = FALSE;
+
+  /**
+   * The non-route uri.
    *
-   * Only used if self::$external is TRUE.
+   * Only used if self::$unrouted is TRUE.
    *
    * @var string
    */
-  protected $path;
+  protected $uri;
 
   /**
    * Constructs a new Url object.
@@ -102,46 +116,27 @@ public function __construct($route_name, $route_parameters = array(), $options =
     $this->options = $options;
   }
 
+  public static function routed($route_name, $route_parameters = array(), $options = array()) {
+    return new static($route_name, $route_parameters, $options);
+  }
+
+  public static function unrouted($uri, $options = array()) {
+    $url = new static($uri, array(), $options);
+    $url->setUnrouted();
+    return $url;
+  }
+
   /**
-   * Returns the Url object matching a path. READ THE FOLLOWING SECURITY NOTE.
+   * Returns the Url object matching a request.
    *
-   * SECURITY NOTE: The path is not checked to be valid and accessible by the
-   * current user to allow storing and reusing Url objects by different users.
-   * The 'path.validator' service getUrlIfValid() method should be used instead
-   * of this one if validation and access check is desired. Otherwise,
+   * SECURITY NOTE: The request path is not checked to be valid and accessible
+   * by the current user to allow storing and reusing Url objects by different
+   * users. The 'path.validator' service getUrlIfValid() method should be used
+   * instead of this one if validation and access check is desired. Otherwise,
    * 'access_manager' service checkNamedRoute() method should be used on the
    * router name and parameters stored in the Url object returned by this
    * method.
    *
-   * @param string $path
-   *   A path (e.g. 'node/1', 'http://drupal.org').
-   *
-   * @return static
-   *   An Url object. Warning: the object is created even if the current user
-   *   can not access the path.
-   *
-   * @throws \Drupal\Core\Routing\MatchingRouteNotFoundException
-   *   Thrown when the path cannot be matched.
-   */
-  public static function createFromPath($path) {
-    if (UrlHelper::isExternal($path)) {
-      $url = new static($path);
-      $url->setExternal();
-      return $url;
-    }
-
-    // Special case the front page route.
-    if ($path == '<front>') {
-      return new static($path);
-    }
-    else {
-      return static::createFromRequest(Request::create("/$path"));
-    }
-  }
-
-  /**
-   * Returns the Url object matching a request. READ THE SECURITY NOTE ON createFromPath().
-   *
    * @param \Symfony\Component\HttpFoundation\Request $request
    *   A request object.
    *
@@ -163,23 +158,18 @@ public static function createFromRequest(Request $request) {
   }
 
   /**
-   * Sets this Url to be external.
+   * Sets this Url to encapselate an unrouted URI.
    *
    * @return $this
    */
-  protected function setExternal() {
-    $this->external = TRUE;
-
-    // What was passed in as the route name is actually the path.
-    $this->path = $this->routeName;
-
+  protected function setUnrouted() {
+    $this->unrouted = TRUE;
+    // What was passed in as the route name is actually the uri.
+    $this->uri = $this->routeName;
     // Set empty route name and parameters.
     $this->routeName = NULL;
     $this->routeParameters = array();
-    // Flag the path as external so the UrlGenerator does not need to check.
-    $this->options['external'] = TRUE;
-
-    return $this;
+    $this->external = strpos($this->uri, 'base://') !== 0;
   }
 
   /**
@@ -197,10 +187,10 @@ public function isExternal() {
    * @return string
    *
    * @throws \UnexpectedValueException.
-   *   If this is an external URL with no corresponding route.
+   *   If this is an URI with no corresponding route.
    */
   public function getRouteName() {
-    if ($this->isExternal()) {
+    if ($this->unrouted) {
       throw new \UnexpectedValueException('External URLs do not have an internal route name.');
     }
 
@@ -213,10 +203,10 @@ public function getRouteName() {
    * @return array
    *
    * @throws \UnexpectedValueException.
-   *   If this is an external URL with no corresponding route.
+   *   If this is an URI with no corresponding route.
    */
   public function getRouteParameters() {
-    if ($this->isExternal()) {
+    if ($this->unrouted) {
       throw new \UnexpectedValueException('External URLs do not have internal route parameters.');
     }
 
@@ -230,10 +220,13 @@ public function getRouteParameters() {
    *   The array of parameters.
    *
    * @return $this
+   *
+   * @throws \UnexpectedValueException.
+   *   If this is an URI with no corresponding route.
    */
   public function setRouteParameters($parameters) {
-    if ($this->isExternal()) {
-      throw new \Exception('External URLs do not have route parameters.');
+    if ($this->unrouted) {
+      throw new \UnexpectedValueException('External URLs do not have route parameters.');
     }
     $this->routeParameters = $parameters;
     return $this;
@@ -248,10 +241,13 @@ public function setRouteParameters($parameters) {
    *   The route parameter.
    *
    * @return $this
+   *
+   * @throws \UnexpectedValueException.
+   *   If this is an URI with no corresponding route.
    */
   public function setRouteParameter($key, $value) {
-    if ($this->isExternal()) {
-      throw new \Exception('External URLs do not have route parameters.');
+    if ($this->unrouted) {
+      throw new \UnexpectedValueException('External URLs do not have route parameters.');
     }
     $this->routeParameters[$key] = $value;
     return $this;
@@ -312,22 +308,22 @@ public function setOption($name, $value) {
   }
 
   /**
-   * Returns the external path of the URL.
+   * Returns the URI of the URL.
    *
-   * Only to be used if self::$external is TRUE.
+   * Only to be used if self::$unrouted is TRUE.
    *
    * @return string
-   *   The external path.
+   *   A URI not connected to a route. May be an external URL.
    *
    * @throws \UnexpectedValueException
-   *   Thrown when the path was requested for an internal URL.
+   *   Thrown when the uri was requested for route.
    */
-  public function getPath() {
-    if (!$this->isExternal()) {
-      throw new \UnexpectedValueException('Internal URLs do not have external paths.');
+  public function getUri() {
+    if (!$this->unrouted) {
+      throw new \UnexpectedValueException('Internal routes do not have uris.');
     }
 
-    return $this->path;
+    return $this->uri;
   }
 
   /**
@@ -344,11 +340,11 @@ public function setAbsolute($absolute = TRUE) {
   }
 
   /**
-   * Generates the path for this Url object.
+   * Generates the uri for this Url object.
    */
   public function toString() {
-    if ($this->isExternal()) {
-      return $this->urlGenerator()->generateFromPath($this->getPath(), $this->getOptions());
+    if ($this->unrouted) {
+      return $this->unroutedUrlAssembler()->assemble($this->getUri(), $this->getOptions());
     }
 
     return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions());
@@ -363,7 +359,7 @@ public function toString() {
   public function toArray() {
     if ($this->isExternal()) {
       return array(
-        'path' => $this->getPath(),
+        'path' => $this->getUri(),
         'options' => $this->getOptions(),
       );
     }
@@ -383,9 +379,9 @@ public function toArray() {
    *   An associative array suitable for a render array.
    */
   public function toRenderArray() {
-    if ($this->isExternal()) {
+    if ($this->unrouted) {
       return array(
-        '#href' => $this->getPath(),
+        '#href' => $this->getUri(),
         '#options' => $this->getOptions(),
       );
     }
@@ -408,13 +404,13 @@ public function toRenderArray() {
    *   The internal path for this route.
    *
    * @throws \UnexpectedValueException.
-   *   If this is an external URL with no corresponding system path.
+   *   If this is an URI with no corresponding system path.
    *
    * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
    *   System paths should not be used - use route names and parameters.
    */
   public function getInternalPath() {
-    if ($this->isExternal()) {
+    if ($this->unrouted) {
       throw new \UnexpectedValueException('External URLs do not have internal representations.');
     }
     return $this->urlGenerator()->getPathFromRoute($this->getRouteName(), $this->getRouteParameters());
@@ -473,6 +469,19 @@ protected function urlGenerator() {
   }
 
   /**
+   * Gets the URL builder for non-Drupal URLs.
+   *
+   * @return \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+   *   The URL builder.
+   */
+  protected function unroutedUrlAssembler() {
+    if (!$this->urlAssember) {
+      $this->urlAssember = \Drupal::service('unrouted_url_assembler');
+    }
+    return $this->urlAssember;
+  }
+
+  /**
    * Sets the URL generator.
    *
    * @param \Drupal\Core\Routing\UrlGeneratorInterface
@@ -485,4 +494,17 @@ public function setUrlGenerator(UrlGeneratorInterface $url_generator) {
     return $this;
   }
 
+  /**
+   * Sets the unrouted URL assembler.
+   *
+   * @param \Drupal\Core\Utility\UnroutedUrlAssemblerInterface
+   *   The unrouted URL assembler.
+   *
+   * @return $this
+   */
+  public function setUnroutedUrlAssembler(UnroutedUrlAssemblerInterface $url_assembler) {
+    $this->urlAssember = $url_assembler;
+    return $this;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
new file mode 100644
index 0000000..05fb19f
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssembler.php
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Utility\UnroutedUrlAssembler.
+ */
+
+namespace Drupal\Core\Utility;
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Provides a way to build external or non Drupal local domain URLs.
+ *
+ * It takes into account configured safe HTTP protocols.
+ */
+class UnroutedUrlAssembler implements UnroutedUrlAssemblerInterface {
+
+  /**
+   * A request stack object.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   *  Constructs a new unroutedUrlAssembler object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactoryInterface $config
+   *    The config factory.
+   * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack
+   *   A request stack object.
+   */
+  public function __construct(RequestStack $request_stack, ConfigFactoryInterface $config) {
+    $allowed_protocols = $config->get('system.filter')->get('protocols') ?: ['http', 'https'];
+    UrlHelper::setAllowedProtocols($allowed_protocols);
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * This is a helper function that calls buildExternalUrl() or buildLocalUrl()
+   * based on a check of whether the path is a valid external URL.
+   */
+  public function assemble($uri, array $options = []) {
+    // Note that UrlHelper::isExternal will return FALSE if the $uri has a
+    // disallowed protocol.  This is later made safe since we always add at
+    // least a leading slash.
+    if (strpos($uri, 'base://') === 0) {
+      return $this->buildLocalUrl($uri, $options);
+    }
+    elseif (UrlHelper::isExternal($uri)) {
+      // 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.');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function buildExternalUrl($uri, array $options = []) {
+    $this->addOptionDefaults($options);
+    // Split off the fragment.
+    if (strpos($uri, '#') !== FALSE) {
+      list($uri, $old_fragment) = explode('#', $uri, 2);
+      // If $options contains no fragment, take it over from the path.
+      if (isset($old_fragment) && !$options['fragment']) {
+        $options['fragment'] = '#' . $old_fragment;
+      }
+    }
+
+    if (isset($options['https'])) {
+      if ($options['https'] === TRUE) {
+        $uri = str_replace('http://', 'https://', $uri);
+      }
+      elseif ($options['https'] === FALSE) {
+        $uri = str_replace('https://', 'http://', $uri);
+      }
+    }
+    // Append the query.
+    if ($options['query']) {
+      $uri .= (strpos($uri, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']);
+    }
+    // Reassemble.
+    return $uri . $options['fragment'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function buildLocalUrl($uri, array $options = []) {
+    $this->addOptionDefaults($options);
+    $request = $this->requestStack->getCurrentRequest();
+
+    // Remove the base:// scheme.
+    $uri = substr($uri, 7);
+    // Add any subdirectory where Drupal is installed.
+    $current_base_path = $request->getBasePath() . '/';
+
+    if ($options['absolute']) {
+      $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path;
+      if (isset($options['https'])) {
+        if (!empty($options['https'])) {
+          $base = str_replace('http://', 'https://', $current_base_url);
+          $options['absolute'] = TRUE;
+        }
+        else {
+          $base = str_replace('https://', 'http://', $current_base_url);
+          $options['absolute'] = TRUE;
+        }
+      }
+      else {
+        $base = $current_base_url;
+      }
+    }
+    else {
+      $base = $current_base_path;
+    }
+
+    $prefix = empty($uri) ? rtrim($options['prefix'], '/') : $options['prefix'];
+
+    $uri = str_replace('%2F', '/', rawurlencode($prefix . $uri));
+    $query = $options['query'] ? ('?' . UrlHelper::buildQuery($options['query'])) : '';
+    return $base . $options['script'] . $uri . $query . $options['fragment'];
+  }
+
+  /**
+   * Merges in default defaults
+   *
+   * @param array $options
+   *   The options to merge in the defaults.
+   */
+  protected function addOptionDefaults(array &$options) {
+    // Merge in defaults.
+    $options += [
+      'fragment' => '',
+      'query' => [],
+      'absolute' => FALSE,
+      'prefix' => '',
+      'script' => '',
+    ];
+
+    if (isset($options['fragment']) && $options['fragment'] !== '') {
+      $options['fragment'] = '#' . $options['fragment'];
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php b/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php
new file mode 100644
index 0000000..d8d0e64
--- /dev/null
+++ b/core/lib/Drupal/Core/Utility/UnroutedUrlAssemblerInterface.php
@@ -0,0 +1,54 @@
+<?php
+/**
+ * @file
+ * Contains Drupal\Core\Utility\UnroutedUrlAssemblerInterface.
+ */
+
+namespace Drupal\Core\Utility;
+
+/**
+ * Provides a way to build external or non Durpal local domain URLs.
+ */
+interface UnroutedUrlAssemblerInterface {
+
+  /**
+   * Builds a domain-local or external URL from a path or URL.
+   *
+   * For actual implementations the logic probably has to be splitted up
+   * between domain-local and external URLs.
+   *
+   * @param string $uri
+   *   A path on the same domain or external URL being linked to, such as "foo"
+   *    or "http://example.com/foo".
+   *   - If you provide a full URL, it will be considered an external URL as
+   *     long as it has an allowed protocol.
+   *   - If you provide only a path (e.g. "foo"), it will be
+   *     considered a URL local to the same domain. Additional query
+   *     arguments for local paths must be supplied in $options['query'], not
+   *     included in $path.
+   *   - If your external URL contains a query (e.g. http://example.com/foo?a=b),
+   *     then you can either URL encode the query keys and values yourself and
+   *     include them in $path, or use $options['query'] to let this method
+   *     URL encode them.
+   *
+   * @param array $options
+   *   (optional) An associative array of additional options, with the following
+   *   elements:
+   *   - 'query': An array of query key/value-pairs (without any URL-encoding) to
+   *     append to the URL.
+   *   - 'fragment': A fragment identifier (named anchor) to append to the URL.
+   *     Do not include the leading '#' character.
+   *   - 'absolute': Defaults to FALSE. Whether to force the output to be an
+   *     absolute link (beginning with http:). Useful for links that will be
+   *     displayed outside the site, such as in an RSS feed.
+   *   - 'https': Whether this URL should point to a secure location. If not
+   *     defined, the current scheme is used, so the user stays on HTTP or HTTPS
+   *     respectively. TRUE enforces HTTPS and FALSE enforces HTTP, but HTTPS can
+   *     only be enforced when the variable 'https' is set to TRUE.
+   *
+   * @return
+   *   A string containing a relative or absolute URL.
+   */
+  public function assemble($uri, array $options = array());
+
+}
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index aecd844..5e85078 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -26,6 +26,7 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
       $output .= '<p>' . t('The Aggregator module is an on-site syndicator and news reader that gathers and displays fresh content from RSS-, RDF-, and Atom-based feeds made available across the web. Thousands of sites (particularly news sites and blogs) publish their latest headlines in feeds, using a number of standardized XML-based formats. For more information, see the <a href="!aggregator-module">online documentation for the Aggregator module</a>.', array('!aggregator-module' => 'https://drupal.org/documentation/modules/aggregator')) . '</p>';
       $output .= '<h3>' . t('Uses') . '</h3>';
       $output .= '<dl>';
+      // Check if the aggregator sources View is enabled.
       if ($url = $path_validator->getUrlIfValid('aggregator/sources')) {
         $output .= '<dt>' . t('Viewing feeds') . '</dt>';
         $output .= '<dd>' . t('Users view feed content in the <a href="!aggregator">main aggregator display</a>, or by <a href="!aggregator-sources">their source</a> (usually via an RSS feed reader). The most recent content in a feed can be displayed as a block through the <a href="!admin-block">Blocks administration page</a>.', array('!aggregator' => \Drupal::url('aggregator.page_last'), '!aggregator-sources' => $url->toString(), '!admin-block' => \Drupal::url('block.admin_display'))) . '</dd>';
@@ -33,6 +34,7 @@ function aggregator_help($route_name, RouteMatchInterface $route_match) {
       $output .= '<dt>' . t('Adding, editing, and deleting feeds') . '</dt>';
       $output .= '<dd>' . t('Administrators can add, edit, and delete feeds, and choose how often to check each feed for newly updated items on the <a href="!feededit">Feed aggregator administration page</a>.', array('!feededit' => \Drupal::url('aggregator.admin_overview'))) . '</dd>';
       $output .= '<dt>' . t('<abbr title="Outline Processor Markup Language">OPML</abbr> integration') . '</dt>';
+      // Check if the aggregator opml View is enabled.
       if ($url = $path_validator->getUrlIfValid('aggregator/opml')) {
         $output .= '<dd>' . t('A <a href="!aggregator-opml">machine-readable OPML file</a> of all feeds is available. OPML is an XML-based file format used to share outline-structured information such as a list of RSS feeds. Feeds can also be <a href="!import-opml">imported via an OPML file</a>.', array('!aggregator-opml' => $url->toString(), '!import-opml' => \Drupal::url('aggregator.opml_add'))) . '</dd>';
       }
diff --git a/core/modules/aggregator/src/Entity/Item.php b/core/modules/aggregator/src/Entity/Item.php
index 5df7013..cb7f0b9 100644
--- a/core/modules/aggregator/src/Entity/Item.php
+++ b/core/modules/aggregator/src/Entity/Item.php
@@ -245,7 +245,7 @@ public function getListCacheTags() {
    * Entity URI callback.
    */
   public static function buildUri(ItemInterface $item) {
-    return Url::createFromPath($item->getLink());
+    return Url::unrouted($item->getLink());
   }
 
 }
diff --git a/core/modules/field_ui/src/FieldUI.php b/core/modules/field_ui/src/FieldUI.php
index 77ec651..be74fff 100644
--- a/core/modules/field_ui/src/FieldUI.php
+++ b/core/modules/field_ui/src/FieldUI.php
@@ -51,15 +51,15 @@ public static function getNextDestination(array $destinations) {
       $next_destination += array(
         'route_parameters' => array(),
       );
-      $next_destination = new Url($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
+      $next_destination = Url::routed($next_destination['route_name'], $next_destination['route_parameters'], $next_destination['options']);
     }
     else {
       $options = UrlHelper::parse($next_destination);
       if ($destinations) {
         $options['query']['destinations'] = $destinations;
       }
-      $next_destination = Url::createFromPath($options['path']);
-      $next_destination->setOptions($options);
+      // Redirect to any given path within the same domain.
+      $next_destination = Url::unrouted('base://' . $options['path']);
     }
     return $next_destination;
   }
diff --git a/core/modules/link/link.module b/core/modules/link/link.module
index d33181e..c9837a3 100644
--- a/core/modules/link/link.module
+++ b/core/modules/link/link.module
@@ -69,6 +69,6 @@ function template_preprocess_link_formatter_link_separate(&$variables) {
     $variables['link'] = \Drupal::linkGenerator()->generateFromUrl($variables['url_title'], $variables['url']);
   }
   else {
-    $variables['link'] = l($variables['url_title'], $variables['url']->getPath(), $variables['url']->getOptions());
+    $variables['link'] = l($variables['url_title'], $variables['url']->getUri(), $variables['url']->getOptions());
   }
 }
diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
index 05caf97..f1d179f 100644
--- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
+++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php
@@ -163,7 +163,7 @@ public function viewElements(FieldItemListInterface $items) {
           '#options' => $url->getOptions(),
         );
         if ($url->isExternal()) {
-          $element[$delta]['#href'] = $url->getPath();
+          $element[$delta]['#href'] = $url->getUri();
         }
         else {
           $element[$delta]['#route_name'] = $url->getRouteName();
@@ -206,11 +206,10 @@ protected function buildUrl(LinkItemInterface $item) {
     }
 
     if ($item->isExternal()) {
-      $url = Url::createFromPath($item->url);
-      $url->setOptions($options);
+      $url = Url::unrouted($item->url, $options);
     }
     else {
-      $url = new Url($item->route_name, (array) $item->route_parameters, (array) $options);
+      $url = Url::routed($item->route_name, (array) $item->route_parameters, (array) $options);
     }
 
     return $url;
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
index c39d156..b44fffc 100644
--- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
@@ -108,7 +108,7 @@ public function getUrlObject() {
     else {
       $path = $this->getUrl();
       if (isset($path)) {
-        $url = Url::createFromPath($path);
+        $url = Url::unrouted($path);
       }
       else {
         $url = new Url('<front>');
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
index 2d37566..4fb93b2 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -198,7 +198,7 @@ public function extractFormValues(array &$form, FormStateInterface $form_state)
 
     if ($extracted) {
       if ($extracted->isExternal()) {
-        $new_definition['url'] = $extracted->getPath();
+        $new_definition['url'] = $extracted->getUri();
       }
       else {
         $new_definition['route_name'] = $extracted->getRouteName();
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index a3e15e7..fa1e1e0 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -14,6 +14,7 @@
 use Drupal\Core\Field\BaseFieldDefinition;
 use Drupal\Core\Url;
 use Drupal\shortcut\ShortcutInterface;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Defines the shortcut entity class.
@@ -135,7 +136,7 @@ public static function preCreate(EntityStorageInterface $storage, array &$values
   public function preSave(EntityStorageInterface $storage) {
     parent::preSave($storage);
 
-    $url = Url::createFromPath($this->path->value);
+    $url = Url::createFromRequest(Request::create("/{$this->path->value}"));
     $this->setRouteName($url->getRouteName());
     $this->setRouteParams($url->getRouteParameters());
   }
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 3f19f14..bc88307 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -452,7 +452,7 @@ function system_authorized_get_url(array $options = array()) {
   // Force HTTPS if available, regardless of what the caller specifies.
   $options['https'] = TRUE;
   // Prefix with $base_url so url() treats it as an external link.
-  $url = Url::createFromPath($base_url . '/core/authorize.php');
+  $url = Url::unrouted('base://core/authorize.php');
   $url_options = $url->getOptions();
   $url->setOptions($options + $url_options);
   return $url;
diff --git a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
index 7fc352e..3eb9cc4 100644
--- a/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
+++ b/core/modules/system/tests/modules/form_test/src/Form/FormTestRedirectForm.php
@@ -54,7 +54,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     if (!$form_state->isValueEmpty('redirection')) {
       if (!$form_state->isValueEmpty('destination')) {
-        $form_state->setRedirectUrl(Url::createFromPath($GLOBALS['base_url'] . '/' . $form_state->getValue('destination')));
+        $form_state->setRedirectUrl(Url::unrouted('base://' . $form_state->getValue('destination')));
       }
     }
     else {
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index b9174bc..815d1cf 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -298,7 +298,7 @@ function toolbar_menu_navigation_links(array $tree) {
     $url = $link->getUrlObject();
     if ($url->isExternal()) {
       // This is an unusual case, so just get a distinct, safe string.
-      $id = substr(Crypt::hashBase64($url->getPath()), 0, 16);
+      $id = substr(Crypt::hashBase64($url->getUri()), 0, 16);
     }
     else {
       $id = str_replace(array('.', '<', '>'), array('-', '', ''), $url->getRouteName());
diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc
index 9ee4dbf..e885ce5 100644
--- a/core/modules/update/update.report.inc
+++ b/core/modules/update/update.report.inc
@@ -135,7 +135,7 @@ function template_preprocess_update_project_status(&$variables) {
 
   // Set the project title and URL.
   $variables['title'] = (isset($project['title'])) ? $project['title'] : $project['name'];
-  $variables['url'] = (isset($project['link'])) ? url($project['link']) : NULL;
+  $variables['url'] = (isset($project['link'])) ? \Drupal::service('unrouted_url_assembler')->assemble($project['link']) : NULL;
 
   $variables['install_type'] = $project['install_type'];
   if ($project['install_type'] == 'dev' && !empty($project['datestamp'])) {
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e33cd28..65b4b04 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -590,9 +590,9 @@ function template_preprocess_username(&$variables) {
     $variables['link_path'] = $account->homepage;
     $variables['homepage'] = $account->homepage;
   }
-  // We have a link path, so we should generate a link using url().
+  // We have a link path, so we should generate a URL.
   if (isset($variables['link_path'])) {
-    $variables['attributes']['href'] = url($variables['link_path'], $variables['link_options']);
+    $variables['attributes']['href'] = \Drupal::service('unrouted_url_assembler')->assemble($variables['link_path'], $variables['link_options']);
   }
 }
 
diff --git a/core/modules/views_ui/src/ViewEditForm.php b/core/modules/views_ui/src/ViewEditForm.php
index 12083cf..d236756 100644
--- a/core/modules/views_ui/src/ViewEditForm.php
+++ b/core/modules/views_ui/src/ViewEditForm.php
@@ -322,10 +322,7 @@ public function save(array $form, FormStateInterface $form_state) {
           $query->remove('destination');
         }
       }
-      if (!UrlHelper::isExternal($destination)) {
-        $destination = $GLOBALS['base_url'] . '/' . $destination;
-      }
-      $form_state->setRedirectUrl(Url::createFromPath($destination));
+      $form_state->setRedirectUrl(Url::createFromRequest(Request::create("/$destination")));
     }
 
     $view->save();
diff --git a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
index 57a51d4..5073689 100644
--- a/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
+++ b/core/tests/Drupal/Tests/Core/ExternalUrlTest.php
@@ -20,11 +20,11 @@
 class ExternalUrlTest extends UnitTestCase {
 
   /**
-   * The URL generator
+   * The URL assembler
    *
    * @var \Drupal\Core\Routing\UrlGeneratorInterface|\PHPUnit_Framework_MockObject_MockObject
    */
-  protected $urlGenerator;
+  protected $urlAssembler;
 
   /**
    * The router.
@@ -46,15 +46,15 @@ class ExternalUrlTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
-    $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
-    $this->urlGenerator->expects($this->any())
-      ->method('generateFromPath')
+    $this->urlAssembler = $this->getMock('Drupal\Core\Utility\UnroutedUrlAssemblerInterface');
+    $this->urlAssembler->expects($this->any())
+      ->method('assemble')
       ->will($this->returnArgument(0));
 
     $this->router = $this->getMock('Drupal\Tests\Core\Routing\TestRouterInterface');
     $container = new ContainerBuilder();
     $container->set('router.no_access_checks', $this->router);
-    $container->set('url_generator', $this->urlGenerator);
+    $container->set('unrouted_url_assembler', $this->urlAssembler);
     \Drupal::setContainer($container);
   }
 
@@ -65,7 +65,7 @@ protected function setUp() {
    * @covers ::setExternal()
    */
   public function testCreateFromPath() {
-    $url = Url::createFromPath($this->path);
+    $url = Url::unrouted($this->path);
     $this->assertInstanceOf('Drupal\Core\Url', $url);
     $this->assertTrue($url->isExternal());
     return $url;
@@ -172,8 +172,8 @@ public function testGetInternalPath(Url $url) {
    *
    * @covers ::getPath()
    */
-  public function testGetPath(Url $url) {
-    $this->assertNotNull($url->getPath());
+  public function testGetUri(Url $url) {
+    $this->assertNotNull($url->getUri());
   }
 
   /**
diff --git a/core/tests/Drupal/Tests/Core/UrlTest.php b/core/tests/Drupal/Tests/Core/UrlTest.php
index 133a73c..4f5108f 100644
--- a/core/tests/Drupal/Tests/Core/UrlTest.php
+++ b/core/tests/Drupal/Tests/Core/UrlTest.php
@@ -102,8 +102,8 @@ public function testCreateFromPath() {
 
     $urls = array();
     foreach ($this->map as $index => $values) {
-      $path = trim(array_pop($values), '/');
-      $url = Url::createFromPath($path);
+      $path = array_pop($values);
+      $url = Url::createFromRequest(Request::create("/$path"));
       $this->assertSame($values, array_values($url->toArray()));
       $urls[$index] = $url;
     }
@@ -131,7 +131,7 @@ protected function getRequestConstraint($path) {
    * @covers ::createFromPath()
    */
   public function testCreateFromPathFront() {
-    $url = Url::createFromPath('<front>');
+    $url = Url::routed('<front>');
     $this->assertSame('<front>', $url->getRouteName());
   }
 
@@ -148,7 +148,7 @@ public function testCreateFromPathInvalid() {
       ->with($this->getRequestConstraint('/non-existent'))
       ->will($this->throwException(new ResourceNotFoundException()));
 
-    $this->assertNull(Url::createFromPath('non-existent'));
+    $this->assertNull(Url::createFromRequest(Request::create('/non-existent')));
   }
 
   /**
@@ -227,8 +227,8 @@ public function testGetPathForInternalUrl($urls) {
    * @covers ::getPath
    */
   public function testGetPathForExternalUrl() {
-    $url = Url::createFromPath('http://example.com/test');
-    $this->assertEquals('http://example.com/test', $url->getPath());
+    $url = Url::unrouted('http://example.com/test');
+    $this->assertEquals('http://example.com/test', $url->getUri());
   }
 
   /**
@@ -292,7 +292,7 @@ public function testGetRouteName($urls) {
    * @expectedException \UnexpectedValueException
    */
   public function testGetRouteNameWithExternalUrl() {
-    $url = Url::createFromPath('http://example.com');
+    $url = Url::unrouted('http://example.com');
     $url->getRouteName();
   }
 
@@ -319,7 +319,7 @@ public function testGetRouteParameters($urls) {
    * @expectedException \UnexpectedValueException
    */
   public function testGetRouteParametersWithExternalUrl() {
-    $url = Url::createFromPath('http://example.com');
+    $url = Url::unrouted('http://example.com');
     $url->getRouteParameters();
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 7926b2f..72dd40b 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -151,7 +151,7 @@ public function testGenerateFromUrlExternal() {
       ->method('alter')
       ->with('link', $this->isType('array'));
 
-    $url = Url::createFromPath('http://drupal.org');
+    $url = Url::unrouted('http://drupal.org');
     $url->setUrlGenerator($this->urlGenerator);
     $url->setOption('set_active_class', TRUE);
 
diff --git a/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
new file mode 100644
index 0000000..492fb79
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Utility/UnroutedUrlAssemblerTest.php
@@ -0,0 +1,129 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Utility\UnroutedUrlAssemblerTest.
+ */
+
+namespace Drupal\Tests\Core\Utility;
+
+use Drupal\Core\Utility\UnroutedUrlAssembler;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * @coversDefaultClass \Drupal\Core\Utility\UnroutedUrlAssembler
+ * @group Utility
+ */
+class UnroutedUrlAssemblerTest extends UnitTestCase {
+
+  /**
+   * The request stack.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   * The mocked config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactoryInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $configFactory;
+
+  /**
+   * The tested unrouted url assembler.
+   *
+   * @var \Drupal\Core\Utility\UnroutedUrlAssembler
+   */
+  protected $unroutedUrlAssembler;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->requestStack = new RequestStack();
+    $this->configFactory = $this->getConfigFactoryStub(['system.filter' => []]);
+    $this->unroutedUrlAssembler = new UnroutedUrlAssembler($this->requestStack, $this->configFactory);
+  }
+
+  /**
+   * @covers ::assemble
+   * @expectedException \InvalidArgumentException
+   */
+  public function testAssembleWithNeitherExternalNokDomainLocalUri() {
+    $this->unroutedUrlAssembler->assemble('wrong-url');
+  }
+
+  /**
+   * @covers ::assemble
+   * @covers ::buildExternalUrl
+   *
+   * @dataProvider providerTestAssembleWithExternalUrl
+   */
+  public function testAssembleWithExternalUrl($uri, array $options, $expected) {
+   $this->assertEquals($expected, $this->unroutedUrlAssembler->assemble($uri, $options));
+  }
+
+  /**
+   * Provides test data for testAssembleWithExternalUrl
+   */
+  public function providerTestAssembleWithExternalUrl() {
+    return [
+      ['http://example.com/test', [], 'http://example.com/test'],
+      ['http://example.com/test', ['fragment' => 'example'], 'http://example.com/test#example'],
+      ['http://example.com/test', ['fragment' => 'example'], 'http://example.com/test#example'],
+      ['http://example.com/test', ['query' => ['foo' => 'bar']], 'http://example.com/test?foo=bar'],
+      ['http://example.com/test', ['https' => TRUE], 'https://example.com/test'],
+      ['https://example.com/test', ['https' => FALSE], 'http://example.com/test'],
+      ['https://example.com/test?foo=1#bar', [], 'https://example.com/test?foo=1#bar'],
+    ];
+  }
+
+  /**
+   * @covers ::assemble
+   * @covers::buildLocalUrl
+   *
+   * @dataProvider providerTestAssembleWithLocalUri
+   */
+  public function testAssembleWithLocalUri($uri, array $options, $subdir, $expected) {
+    $server = [];
+    if ($subdir) {
+      // Setup a fake request which looks like a Drupal installed under the
+      // subdir "subdir" on the domain www.example.com.
+      // To reproduce the values install Drupal like that and use a debugger.
+      $server = [
+        'SCRIPT_NAME' => '/subdir/index.php',
+        'SCRIPT_FILENAME' => DRUPAL_ROOT . '/index.php',
+        'SERVER_NAME' => 'http://www.example.com',
+      ];
+      $request = Request::create('/subdir');
+    }
+    else {
+      $request = Request::create('/');
+    }
+    $request->server->add($server);
+    $this->requestStack->push($request);
+
+    $this->assertEquals($expected, $this->unroutedUrlAssembler->assemble($uri, $options));
+  }
+
+  /**
+   * @return array
+   */
+  public function providerTestAssembleWithLocalUri() {
+    return [
+      ['base://example', [], FALSE, '/example'],
+      ['base://example', ['query' => ['foo' => 'bar']], FALSE, '/example?foo=bar'],
+      ['base://example', ['fragment' => 'example', ], FALSE, '/example#example'],
+      ['base://example', [], TRUE, '/subdir/example'],
+      ['base://example', ['query' => ['foo' => 'bar']], TRUE, '/subdir/example?foo=bar'],
+      ['base://example', ['fragment' => 'example', ], TRUE, '/subdir/example#example'],
+    ];
+  }
+
+}
+
