diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
index a4ec691a94b1e8c9ffaa92642b2ef1c62445610a..f29e0b01378824a3ac790eadd7a1a889a595c04c 100644
--- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Language\LanguageManager;
 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
 use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
@@ -41,6 +42,10 @@ public function __construct(LanguageManager $language_manager) {
    *   The event to process.
    */
   public function onRespond(FilterResponseEvent $event) {
+    if ($event->getRequestType() !== HttpKernelInterface::MASTER_REQUEST) {
+      return;
+    }
+
     $response = $event->getResponse();
 
     // Set the X-UA-Compatible HTTP header to force IE to use the most recent
diff --git a/core/lib/Drupal/Core/HtmlPageController.php b/core/lib/Drupal/Core/HtmlPageController.php
index 7d14ac000973389259a2d411e0df2a91ae1f312f..ab9af59e37c16548f92e06b27bbb5c3c543c882f 100644
--- a/core/lib/Drupal/Core/HtmlPageController.php
+++ b/core/lib/Drupal/Core/HtmlPageController.php
@@ -52,7 +52,7 @@ public function content(Request $request, $_content) {
     // require an _internal route.  For examples, see:
     // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Resources/config/routing/internal.xml
     // https://github.com/symfony/symfony/blob/master/src/Symfony/Bundle/FrameworkBundle/Controller/InternalController.php
-    $attributes = $request->attributes;
+    $attributes = clone $request->attributes;
     $controller = $_content;
 
     // We need to clean off the derived information and such so that the
@@ -62,8 +62,12 @@ public function content(Request $request, $_content) {
 
     $response = $this->container->get('http_kernel')->forward($controller, $attributes->all(), $request->query->all());
 
-    $page_content = $response->getContent();
+    // For successful (HTTP status 200) responses, decorate with blocks.
+    if ($response->isOk()) {
+      $page_content = $response->getContent();
+      $response = new Response(drupal_render_page($page_content));
+    }
 
-    return new Response(drupal_render_page($page_content));
+    return $response;
   }
 }
diff --git a/core/lib/Drupal/Core/Routing/PathMatcher.php b/core/lib/Drupal/Core/Routing/PathMatcher.php
index f037d9acb4100ec70c77c7e7053cd0d77ac6ae2e..9b5bd5ede224f9a8347aefe5a1f8ba749c1ceb08 100644
--- a/core/lib/Drupal/Core/Routing/PathMatcher.php
+++ b/core/lib/Drupal/Core/Routing/PathMatcher.php
@@ -56,7 +56,21 @@ public function __construct(Connection $connection, $table = 'router') {
    */
   public function matchRequestPartial(Request $request) {
 
-    $path = rtrim($request->getPathInfo(), '/');
+    // The 'system_path' has language prefix stripped and path alias resolved,
+    // whereas getPathInfo() returns the requested path. In Drupal, the request
+    // always contains a system_path attribute, but this component may get
+    // adopted by non-Drupal projects. Some unit tests also skip initializing
+    // 'system_path'.
+    // @todo Consider abstracting this to a separate object.
+    if ($request->attributes->has('system_path')) {
+      // system_path never has leading or trailing slashes.
+      $path = '/' . $request->attributes->get('system_path');
+    }
+    else {
+      // getPathInfo() always has leading slash, and might or might not have a
+      // trailing slash.
+      $path = rtrim($request->getPathInfo(), '/');
+    }
 
     $parts = array_slice(array_filter(explode('/', $path)), 0, MatcherDumper::MAX_PARTS);
 
diff --git a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php b/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
index 078025085dacdc9aae1004c90640d34c55de1e5c..67691071da810ab524663df897448fc3a2660d41 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Routing/PathMatcherTest.php
@@ -299,4 +299,27 @@ function testOutlinePathNoMatch() {
 
   }
 
+  /**
+   * Confirms that system_path attribute overrides request path.
+   */
+  function testSystemPathMatch() {
+    $connection = Database::getConnection();
+    $matcher = new PathMatcher($connection, 'test_routes');
+
+    $this->fixtures->createTables($connection);
+
+    $dumper = new MatcherDumper($connection, 'test_routes');
+    $dumper->addRoutes($this->fixtures->sampleRouteCollection());
+    $dumper->dump();
+
+    $request = Request::create('/path/one', 'GET');
+    $request->attributes->set('system_path', 'path/two');
+
+    $routes = $matcher->matchRequestPartial($request);
+
+    foreach ($routes as $route) {
+      $this->assertEqual($route->getPattern(), '/path/two', 'Found path has correct pattern');
+    }
+  }
+
 }
diff --git a/core/modules/user/lib/Drupal/user/UserRouteController.php b/core/modules/user/lib/Drupal/user/UserRouteController.php
new file mode 100644
index 0000000000000000000000000000000000000000..0cb5a1507cb04f57ced284c80cce9d54b7ba05fa
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/UserRouteController.php
@@ -0,0 +1,35 @@
+<?php
+
+/**
+ * @file
+ * Contains of Drupal\user\UserRouteController.
+ */
+
+namespace Drupal\user;
+
+use Symfony\Component\DependencyInjection\ContainerAware;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * Returns responses for User module routes.
+ */
+class UserRouteController extends ContainerAware {
+
+  /**
+   * Returns the user registration form.
+   *
+   * @return array
+   *   A renderable array containing the user registration form.
+   */
+  public function register() {
+    // @todo Remove once access control is integrated with new routing system:
+    //   http://drupal.org/node/1793520.
+    if (!user_register_access()) {
+      throw new AccessDeniedHttpException();
+    }
+
+    $account = entity_create('user', array());
+    return entity_get_form($account, 'register');
+  }
+
+}
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index d576afc2c512046363935b8db44fde5dc2e752b4..c399e0ac03c053a69e9d9c90c70a05bdd6286a28 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1070,11 +1070,6 @@ function user_is_logged_in() {
   return (bool) $GLOBALS['user']->uid;
 }
 
-function user_register() {
-  $account = entity_create('user', array());
-  return entity_get_form($account, 'register');
-}
-
 function user_register_access() {
   return user_is_anonymous() && (config('user.settings')->get('register') != USER_REGISTER_ADMINISTRATORS_ONLY);
 }
@@ -1160,9 +1155,14 @@ function user_menu() {
 
   $items['user/register'] = array(
     'title' => 'Create new account',
-    'page callback' => 'user_register',
-    'access callback' => 'user_register_access',
     'type' => MENU_LOCAL_TASK,
+    // @todo This route is now declared in user.routing.yml, so the below are
+    //   only needed for drupal_valid_path(). Without a non-empty (but not
+    //   necessarily valid) page callback, _menu_router_build() overrides the
+    //   access callback to 0. Remove once drupal_valid_path works with the new
+    //   routing system: http://drupal.org/node/1793520.
+    'page callback' => 'NOT_USED',
+    'access callback' => 'user_register_access',
   );
 
   $items['user/password'] = array(
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
new file mode 100644
index 0000000000000000000000000000000000000000..5e11fab24ef0f0492258ed06ce4d4711c0673a9f
--- /dev/null
+++ b/core/modules/user/user.routing.yml
@@ -0,0 +1,4 @@
+user_register:
+  pattern: '/user/register'
+  defaults:
+    _content: '\Drupal\user\UserRouteController::register'