diff --git a/core/core.services.yml b/core/core.services.yml
index 91a2c2b..ca67ffd 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']]
+  url_builder:
+    class: Drupal\Core\Routing\UrlBuilder
+    arguments: ['@request_stack', '@config.factory' ]
   link_generator:
     class: Drupal\Core\Utility\LinkGenerator
     arguments: ['@url_generator', '@module_handler']
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/Routing/UrlBuilder.php b/core/lib/Drupal/Core/Routing/UrlBuilder.php
new file mode 100644
index 0000000..01fd9cb
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/UrlBuilder.php
@@ -0,0 +1,135 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\Core\Routing\UrlBuilder.
+ */
+
+namespace Drupal\Core\Routing;
+
+use Drupal\Component\Utility\UrlHelper;
+use Drupal\Core\Config\ConfigFactoryInterface;
+use Symfony\Component\HttpFoundation\RequestStack;
+
+/**
+ * Drupal-specific URL Matcher; handles the Drupal "system path" mapping.
+ */
+class UrlBuilder implements UrlBuilderInterface {
+
+  /**
+   * A request stack object.
+   *
+   * @var \Symfony\Component\HttpFoundation\RequestStack
+   */
+  protected $requestStack;
+
+  /**
+   *  Constructs a new UrlBuilder 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') ?: array('http', 'https');
+    UrlHelper::setAllowedProtocols($allowed_protocols);
+    $this->requestStack = $request_stack;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildFromPath($path, array $options = array()) {
+    // Note that UrlHelper::isExternal will return FALSE if the $path has a
+    // disallowed protocol.  This is later made safe since we always add at
+    // least a leading slash.
+    if (strpos($path, 'base://') !== 0 && UrlHelper::isExternal($path)) {
+      // UrlHelper::isExternal() only returns true for safe protocols.
+      return $this->buildExternalUrl($path, $options, FALSE);
+    }
+    return $this->buildLocalUrl($path, $options);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildExternalUrl($path, array $options = array(), $strip_dangerous_protocols = TRUE) {
+    $this->addOptionDefaults($options);
+    // Split off the fragment.
+    if (strpos($path, '#') !== FALSE) {
+      list($path, $old_fragment) = explode('#', $path, 2);
+      // If $options contains no fragment, take it over from the path.
+      if (isset($old_fragment) && !$options['fragment']) {
+        $options['fragment'] = '#' . $old_fragment;
+      }
+    }
+    if ($strip_dangerous_protocols) {
+      $path = UrlHelper::stripDangerousProtocols($path);
+    }
+    // Append the query.
+    if ($options['query']) {
+      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . UrlHelper::buildQuery($options['query']);
+    }
+    // Reassemble.
+    return $path . $options['fragment'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildLocalUrl($path, array $options = array()) {
+    $this->addOptionDefaults($options);
+    $request = $this->requestStack->getCurrentRequest();
+    if (strpos($path, 'base://') === 0) {
+      // Remove the scheme.
+      $path = substr($path, 7);
+      // Add any subdirectory where Drupal is installed.
+      $current_base_path = $request->getBasePath() . '/';
+    }
+    else {
+      $current_base_path = '/';
+    }
+
+    if ($options['absolute']) {
+      $current_base_url = $request->getSchemeAndHttpHost() . $current_base_path;
+      if (isset($options['https'])) {
+        if ($options['https'] === TRUE) {
+          $base = str_replace('http://', 'https://', $current_base_url);
+          $options['absolute'] = TRUE;
+        }
+        elseif ($options['https'] === FALSE) {
+          $base = str_replace('https://', 'http://', $current_base_url);
+          $options['absolute'] = TRUE;
+        }
+      }
+      else {
+        $base = $current_base_url;
+      }
+    }
+    else {
+      $base = $current_base_path;
+    }
+
+    $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
+
+    $path = str_replace('%2F', '/', rawurlencode($prefix . $path));
+    $query = $options['query'] ? ('?' . UrlHelper::buildQuery($options['query'])) : '';
+    return $base . $options['script'] . $path . $query . $options['fragment'];
+  }
+
+  protected function addOptionDefaults(&$options) {
+    // Merge in defaults.
+    $options += array(
+      'fragment' => '',
+      'query' => array(),
+      'absolute' => FALSE,
+      'prefix' => '',
+      'script' => '',
+    );
+
+    if (isset($options['fragment']) && $options['fragment'] !== '') {
+      $options['fragment'] = '#' . $options['fragment'];
+    }
+  }
+}
diff --git a/core/lib/Drupal/Core/Routing/UrlBuilderInterface.php b/core/lib/Drupal/Core/Routing/UrlBuilderInterface.php
new file mode 100644
index 0000000..2434ac0
--- /dev/null
+++ b/core/lib/Drupal/Core/Routing/UrlBuilderInterface.php
@@ -0,0 +1,117 @@
+<?php
+/**
+ * @file
+ * Contains Drupal\Core\Routing\UrlBuilderInterface.
+ */
+
+namespace Drupal\Core\Routing;
+
+
+interface UrlBuilderInterface {
+
+  /**
+   * Builds a domain-local or external URL from a path or URL.
+   *
+   * This is a helper function that calls buildExternalUrl() or buildLocalUrl()
+   * based on a check of whether the path is a valid external URL.
+   *
+   * @param string $path
+   *   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.
+   *   - 'prefix': Only used internally, to modify the path when a language
+   *     dependent URL requires so.
+   *   - 'script': Added to the URL between the base path and the path prefix.
+   *     For example, 'index.php/'.
+   *
+   * @return
+   *   A string containing a relative or absolute URL.
+   */
+  public function buildFromPath($path, array $options = array());
+
+  /**
+   * Builds an external URL.
+   *
+   * @param string $path
+   *   An external URL being linked to such as "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 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.
+   * @param bool $strip_dangerous_protocols
+   *   By default, strip dangerous URL protocols. Set to FALSE to allow any
+   *   value to pass through.
+   *
+   * @return
+   *   A string containing an external URL.
+   */
+  public function buildExternalUrl($path, array $options = array(), $strip_dangerous_protocols = TRUE);
+
+  /**
+   * Builds a domain-local URL from a path.
+   *
+   * Additional query arguments must be supplied in $options['query'], not
+   * included in $path.
+   *
+   * @param string $path
+   *   A path on the same domain. If the path is prefixed with with base://,
+   *   the Drupal base path with be included. This only matters if Drupal is
+   *   installed in a subdomain.
+   *
+   * @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.
+   *   - 'prefix': Only used internally, to modify the path when a language
+   *     dependent URL requires so.
+   *   - 'script': Added to the URL between the base path and the path prefix.
+   *     For example, 'index.php/'.
+   *
+   * @return
+   *   A string containing a relative or absolute URL.
+   */
+  public function buildLocalUrl($path, array $options = array());
+}
diff --git a/core/lib/Drupal/Core/Url.php b/core/lib/Drupal/Core/Url.php
index d97f017..bf1a39a 100644
--- a/core/lib/Drupal/Core/Url.php
+++ b/core/lib/Drupal/Core/Url.php
@@ -28,6 +28,13 @@ class Url {
   protected $urlGenerator;
 
   /**
+   * The URL builder.
+   *
+   * @var \Drupal\Core\Routing\UrlBuilderInterface
+   */
+  protected $urlBuilder;
+
+  /**
    * The access manager
    *
    * @var \Drupal\Core\Access\AccessManagerInterface
@@ -348,7 +355,7 @@ public function setAbsolute($absolute = TRUE) {
    */
   public function toString() {
     if ($this->isExternal()) {
-      return $this->urlGenerator()->generateFromPath($this->getPath(), $this->getOptions());
+      return $this->urlBuilder()->buildExternalUrl($this->getPath(), $this->getOptions());
     }
 
     return $this->urlGenerator()->generateFromRoute($this->getRouteName(), $this->getRouteParameters(), $this->getOptions());
@@ -473,6 +480,19 @@ protected function urlGenerator() {
   }
 
   /**
+   * Gets the URL builder for non-Drupal URLs.
+   *
+   * @return \Drupal\Core\Routing\UrlBuilderInterface
+   *   The URL builder.
+   */
+  protected function urlBuilder() {
+    if (!$this->urlBuilder) {
+      $this->urlBuilder = \Drupal::service('url_builder');
+    }
+    return $this->urlBuilder;
+  }
+
+  /**
    * Sets the URL generator.
    *
    * @param \Drupal\Core\Routing\UrlGeneratorInterface
diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc
index 9ee4dbf..dc7a417 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('url_builder')->buildExternalUrl($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..3e78c09 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('url_builder')->buildFromPath($variables['link_path'], $variables['link_options']);
   }
 }
 
