diff --git a/core/core.services.yml b/core/core.services.yml
index db46b5753f7e5064f72f6a3b1acda51ee9f447f5..03b8c7cf0d5de357d77784dcdc7ef724f9a856b8 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -365,6 +365,12 @@ services:
       - { name: event_subscriber }
     arguments: ['@language_manager']
     scope: request
+  redirect_response_subscriber:
+    class: Drupal\Core\EventSubscriber\RedirectResponseSubscriber
+    arguments: ['@url_generator']
+    tags:
+      - { name: event_subscriber }
+    scope: request
   request_close_subscriber:
     class: Drupal\Core\EventSubscriber\RequestCloseSubscriber
     tags:
diff --git a/core/includes/batch.inc b/core/includes/batch.inc
index 3fe9bb080ef3850ce39eba03c5ef0b4089400785..27fb632a23da4598726ed3413f9f4609b9fdc508 100644
--- a/core/includes/batch.inc
+++ b/core/includes/batch.inc
@@ -15,7 +15,8 @@
  */
 
 use Drupal\Core\Batch\Percentage;
-use \Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Renders the batch processing page based on the current state of the batch.
@@ -34,7 +35,7 @@ function _batch_page() {
     $batch = Drupal::service('batch.storage')->load($_REQUEST['id']);
     if (!$batch) {
       drupal_set_message(t('No active batch.'), 'error');
-      drupal_goto();
+      return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
     }
   }
 
@@ -412,7 +413,7 @@ function _batch_finished() {
   if ($_batch['progressive']) {
     // Revert the 'destination' that was saved in batch_process().
     if (isset($_batch['destination'])) {
-      $_GET['destination'] = $_batch['destination'];
+      Drupal::request()->query->set('destination', $_batch['destination']);
     }
 
     // Determine the target path to redirect to.
@@ -426,7 +427,10 @@ function _batch_finished() {
     }
 
     // Use drupal_redirect_form() to handle the redirection logic.
-    drupal_redirect_form($_batch['form_state']);
+    $redirect = drupal_redirect_form($_batch['form_state']);
+    if (is_object($redirect)) {
+      return $redirect;
+    }
 
     // If no redirection happened, redirect to the originating page. In case the
     // form needs to be rebuilt, save the final $form_state for
@@ -438,6 +442,14 @@ function _batch_finished() {
     if (function_exists($function)) {
       $function($_batch['source_url'], array('query' => array('op' => 'finish', 'id' => $_batch['id'])));
     }
+    elseif ($function === NULL) {
+      // Default to RedirectResponse objects when nothing specified.
+      $url = url($_batch['source_url'], array(
+        'absolute' => TRUE,
+        'query' => array('op' => 'finish', 'id' => $_batch['id']),
+      ));
+      return new RedirectResponse($url);
+    }
   }
 }
 
diff --git a/core/includes/common.inc b/core/includes/common.inc
index e5bf9370109c66596123aefa3b8ab541b9f73466..d8638feaaf6e138840e550f0a5b3ba2c4fba652c 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -503,7 +503,7 @@ function drupal_http_build_query(array $query, $parent = '') {
 }
 
 /**
- * Prepares a 'destination' URL query parameter for use with drupal_goto().
+ * Prepares a 'destination' URL query parameter for use with url().
  *
  * Used to direct the user back to the referring page after completing a form.
  * By default the current URL is returned. If a destination exists in the
@@ -516,7 +516,6 @@ function drupal_http_build_query(array $query, $parent = '') {
  *     not available, the current path.
  *
  * @see current_path()
- * @see drupal_goto()
  */
 function drupal_get_destination() {
   $destination = &drupal_static(__FUNCTION__);
@@ -570,7 +569,6 @@ function drupal_get_destination() {
  *   - 'fragment': The fragment of $url, if existent.
  *
  * @see url()
- * @see drupal_goto()
  * @ingroup php_wrappers
  */
 function drupal_parse_url($url) {
@@ -629,84 +627,6 @@ function drupal_encode_path($path) {
   return str_replace('%2F', '/', rawurlencode($path));
 }
 
-/**
- * Sends the user to a different Drupal page.
- *
- * This issues an on-site HTTP redirect. The function makes sure the redirected
- * URL is formatted correctly.
- *
- * If a destination was specified in the current request's URI (i.e.,
- * $_GET['destination']) then it will override the $path and $options values
- * passed to this function. This provides the flexibility to build a link to
- * user/login and override the default redirection so that the user is
- * redirected to a specific path after logging in:
- * @code
- *   $query = array('destination' => "node/$node->nid");
- *   $link = l(t('Log in'), 'user/login', array('query' => $query));
- * @endcode
- *
- * Drupal will ensure that messages set by drupal_set_message() and other
- * session data are written to the database before the user is redirected.
- *
- * This function ends the request; use it instead of a return in your menu
- * callback.
- *
- * @param $path
- *   (optional) A Drupal path or a full URL, which will be passed to url() to
- *   compute the redirect for the URL.
- * @param $options
- *   (optional) An associative array of additional URL options to pass to url().
- * @param $http_response_code
- *   (optional) The HTTP status code to use for the redirection, defaults to
- *   302. The valid values for 3xx redirection status codes are defined in
- *   @link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3 RFC 2616 @endlink
- *   and the
- *   @link http://tools.ietf.org/html/draft-reschke-http-status-308-07 draft for the new HTTP status codes: @endlink
- *   - 301: Moved Permanently (the recommended value for most redirects).
- *   - 302: Found (default in Drupal and PHP, sometimes used for spamming search
- *     engines).
- *   - 303: See Other.
- *   - 304: Not Modified.
- *   - 305: Use Proxy.
- *   - 307: Temporary Redirect.
- *
- * @see drupal_get_destination()
- * @see url()
- */
-function drupal_goto($path = '', array $options = array(), $http_response_code = 302) {
-  // A destination in $_GET always overrides the function arguments.
-  // We do not allow absolute URLs to be passed via $_GET, as this can be an
-  // attack vector, with the following exception:
-  // - Absolute URLs that point to this site (i.e. same base URL and
-  //   base path) are allowed.
-  if (isset($_GET['destination']) && (!url_is_external($_GET['destination']) || _external_url_is_local($_GET['destination']))) {
-    $destination = drupal_parse_url($_GET['destination']);
-    $path = $destination['path'];
-    $options['query'] = $destination['query'];
-    $options['fragment'] = $destination['fragment'];
-  }
-
-  drupal_alter('drupal_goto', $path, $options, $http_response_code);
-
-  // The 'Location' HTTP header must be absolute.
-  $options['absolute'] = TRUE;
-
-  $url = Drupal::urlGenerator()->generateFromPath($path, $options);
-
-  if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
-    drupal_session_commit();
-  }
-
-  $response = new RedirectResponse($url, $http_response_code);
-  // @todo We should not send the response here: http://drupal.org/node/1668866
-  $response->sendHeaders();
-
-  // The "Location" header sends a redirect status code to the HTTP daemon. In
-  // some cases this can be wrong, so we make sure none of the code below the
-  // drupal_goto() call gets executed upon redirection.
-  exit;
-}
-
 /**
  * Determines if an external URL points to this Drupal installation.
  *
diff --git a/core/includes/form.inc b/core/includes/form.inc
index b23bd2848542636c313f975f88d5a9d61a3c96fe..46897112abe137c3a0c714c4393b41857d52d09a 100644
--- a/core/includes/form.inc
+++ b/core/includes/form.inc
@@ -14,6 +14,10 @@
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Utility\Color;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
 
 /**
  * @defgroup forms Form builder functions
@@ -227,9 +231,8 @@ function drupal_get_form($form_arg) {
  *     errors.
  *   - redirect: Used to redirect the form on submission. It may either be a
  *     string containing the destination URL, or an array of arguments
- *     compatible with drupal_goto(). See drupal_redirect_form() for complete
- *     information.
- *   - no_redirect: If set to TRUE the form will NOT perform a drupal_goto(),
+ *     compatible with url(). See url() for complete information.
+ *   - no_redirect: If set to TRUE the form will NOT perform a redirect,
  *     even if 'redirect' is set.
  *   - method: The HTTP form method to use for finding the input for this form.
  *     May be 'post' or 'get'. Defaults to 'post'. Note that 'get' method
@@ -413,7 +416,12 @@ function drupal_build_form($form_id, &$form_state) {
   //   appropriate information persists to the next page request.
   // All of the handlers in the pipeline receive $form_state by reference and
   // can use it to know or update information about the state of the form.
-  drupal_process_form($form_id, $form, $form_state);
+  $response = drupal_process_form($form_id, $form, $form_state);
+
+  // If the form returns some kind of response, deliver it.
+  if ($response instanceof Response) {
+    _drupal_form_send_response($response);
+  }
 
   // If this was a successful submission of a single-step form or the last step
   // of a multi-step form, then drupal_process_form() issued a redirect to
@@ -858,6 +866,10 @@ function drupal_retrieve_form($form_id, &$form_state) {
   // If $callback was returned by a hook_forms() implementation, call it.
   // Otherwise, call the function named after the form id.
   $form = call_user_func_array($callback, $args);
+  // If the form returns some kind of response, deliver it.
+  if ($form instanceof Response) {
+    _drupal_form_send_response($form);
+  }
   $form['#form_id'] = $form_id;
 
   return $form;
@@ -941,7 +953,10 @@ function drupal_process_form($form_id, &$form, &$form_state) {
         }
 
         $batch['progressive'] = !$form_state['programmed'];
-        batch_process();
+        $response = batch_process();
+        if ($batch['progressive']) {
+          return $response;
+        }
 
         // Execution continues only for programmatic forms.
         // For 'regular' forms, we get redirected to the batch processing
@@ -953,7 +968,10 @@ function drupal_process_form($form_id, &$form, &$form_state) {
       $form_state['executed'] = TRUE;
 
       // Redirect the form based on values in $form_state.
-      drupal_redirect_form($form_state);
+      $redirect = drupal_redirect_form($form_state);
+      if (is_object($redirect)) {
+        return $redirect;
+      }
     }
 
     // Don't rebuild or cache form submissions invoked via drupal_form_submit().
@@ -1245,10 +1263,10 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
  *
  * Usually (for exceptions, see below) $form_state['redirect'] determines where
  * to redirect the user. This can be set either to a string (the path to
- * redirect to), or an array of arguments for drupal_goto(). If
- * $form_state['redirect'] is missing, the user is usually (again, see below for
- * exceptions) redirected back to the page they came from, where they should see
- * a fresh, unpopulated copy of the form.
+ * redirect to), or an array of arguments for url(). If $form_state['redirect']
+ * is missing, the user is usually (again, see below for exceptions) redirected
+ * back to the page they came from, where they should see a fresh, unpopulated
+ * copy of the form.
  *
  * Here is an example of how to set up a form to redirect to the path 'node':
  * @code
@@ -1279,12 +1297,11 @@ function drupal_validate_form($form_id, &$form, &$form_state) {
  *   form builder functions or form validation/submit handlers.
  * - If $form_state['redirect'] is set to FALSE, redirection is disabled.
  * - If none of the above conditions has prevented redirection, then the
- *   redirect is accomplished by calling drupal_goto(), passing in the value of
- *   $form_state['redirect'] if it is set, or the current path if it is
- *   not. drupal_goto() preferentially uses the value of $_GET['destination']
+ *   redirect is accomplished by returning a RedirectResponse, passing in the
+ *   value of $form_state['redirect'] if it is set, or the current path if it is
+ *   not. RedirectResponse preferentially uses the value of $_GET['destination']
  *   (the 'destination' URL query string) if it is present, so this will
- *   override any values set by $form_state['redirect']. Note that during
- *   installation, install_goto() is called in place of drupal_goto().
+ *   override any values set by $form_state['redirect'].
  *
  * @param $form_state
  *   An associative array containing the current state of the form.
@@ -1305,22 +1322,33 @@ function drupal_redirect_form($form_state) {
   if (!empty($form_state['no_redirect'])) {
     return;
   }
-  // Only invoke drupal_goto() if redirect value was not set to FALSE.
+  // Only invoke a redirection if redirect value was not set to FALSE.
   if (!isset($form_state['redirect']) || $form_state['redirect'] !== FALSE) {
     if (isset($form_state['redirect'])) {
       if (is_array($form_state['redirect'])) {
-        call_user_func_array('drupal_goto', $form_state['redirect']);
+        $options = isset($form_state['redirect'][1]) ? $form_state['redirect'][1] : array();
+        // Redirections should always use absolute URLs.
+        $options['absolute'] = TRUE;
+        $status_code =  isset($form_state['redirect'][2]) ? $form_state['redirect'][2] : 302;
+        return new RedirectResponse(url($form_state['redirect'][0], $options), $status_code);
       }
       else {
         // This function can be called from the installer, which guarantees
         // that $redirect will always be a string, so catch that case here
         // and use the appropriate redirect function.
-        $function = drupal_installation_attempted() ? 'install_goto' : 'drupal_goto';
-        $function($form_state['redirect']);
+        if (drupal_installation_attempted()) {
+          install_goto($form_state['redirect']);
+        }
+        else {
+          return new RedirectResponse(url($form_state['redirect'], array('absolute' => TRUE)));
+        }
       }
     }
-    $request = Drupal::request();
-    drupal_goto($request->attributes->get('system_path'), array('query' => $request->query->all()));
+    $url = url(Drupal::request()->attributes->get('system_path'), array(
+      'query' => Drupal::request()->query->all(),
+      'absolute' => TRUE,
+    ));
+    return new RedirectResponse($url);
   }
 }
 
@@ -4845,6 +4873,28 @@ function _form_set_attributes(&$element, $class = array()) {
   }
 }
 
+/**
+ * Triggers kernel.response and sends a form response.
+ *
+ * @deprecated This function is to be used internally by Form API only.
+ *
+ * @param \Symfony\Component\HttpFoundation\Response $response
+ *   A response object.
+ */
+function _drupal_form_send_response(Response $response) {
+  $request = Drupal::request();
+  $kernel = Drupal::service('http_kernel');
+  $event = new FilterResponseEvent($kernel, $request, $kernel::MASTER_REQUEST, $response);
+
+  Drupal::service('event_dispatcher')->dispatch(KernelEvents::RESPONSE, $event);
+  // Prepare and send the response.
+  $event->getResponse()
+    ->prepare($request)
+    ->send();
+  $kernel->terminate($request, $response);
+  exit;
+}
+
 /**
  * @} End of "defgroup form_api".
  */
@@ -5067,9 +5117,6 @@ function batch_set($batch_definition) {
 /**
  * Processes the batch.
  *
- * Unless the batch has been marked with 'progressive' = FALSE, the function
- * issues a drupal_goto and thus ends page execution.
- *
  * This function is generally not needed in form submit handlers;
  * Form API takes care of batches that were set during form submission.
  *
@@ -5080,11 +5127,12 @@ function batch_set($batch_definition) {
  *   URL of the batch processing page.
  * @param $redirect_callback
  *   (optional) Specify a function to be called to redirect to the progressive
- *   processing page. By default drupal_goto() will be used to redirect to a
- *   page which will do the progressive page. Specifying another function will
- *   allow the progressive processing to be processed differently.
+ *   processing page.
+ *
+ * @return \Symfony\Component\HttpFoundation\RedirectResponse|null
+ *   A redirect response if the batch is progressive. No return value otherwise.
  */
-function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'drupal_goto') {
+function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = NULL) {
   $batch =& batch_get();
 
   drupal_theme_initialize();
@@ -5124,11 +5172,12 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'd
       // the generic error message.
       $batch['error_message'] = t('Please continue to <a href="@error_url">the error page</a>', array('@error_url' => url($url, array('query' => array('id' => $batch['id'], 'op' => 'finished')))));
 
-      // Clear the way for the drupal_goto() redirection to the batch processing
-      // page, by saving and unsetting the 'destination', if there is any.
-      if (isset($_GET['destination'])) {
-        $batch['destination'] = $_GET['destination'];
-        unset($_GET['destination']);
+      // Clear the way for the redirection to the batch processing page, by
+      // saving and unsetting the 'destination', if there is any.
+      $request = Drupal::request();
+      if ($request->query->has('destination')) {
+        $batch['destination'] = $request->query->get('destination');
+        $request->query->remove('destination');
       }
 
       // Store the batch.
@@ -5138,8 +5187,14 @@ function batch_process($redirect = NULL, $url = 'batch', $redirect_callback = 'd
       $_SESSION['batches'][$batch['id']] = TRUE;
 
       // Redirect for processing.
-      $function = $batch['redirect_callback'];
-      $function($batch['url'], array('query' => array('op' => 'start', 'id' => $batch['id'])));
+      $options = array('query' => array('op' => 'start', 'id' => $batch['id']));
+      if (($function = $batch['redirect_callback']) && function_exists($function)) {
+        $function($batch['url'], $options);
+      }
+      else {
+        $options['absolute'] = TRUE;
+        return new RedirectResponse(url($batch['url'], $options));
+      }
     }
     else {
       // Non-progressive execution: bypass the whole progressbar workflow
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index e7ee511ad40ded377c38f14456d8bb24e8b8fdad..1ecd819e5819857fd193cd6d0be51cf1bac73893 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -626,7 +626,14 @@ function install_run_task($task, &$install_state) {
       }
       // Process the batch. For progressive batches, this will redirect.
       // Otherwise, the batch will complete.
-      batch_process(install_redirect_url($install_state), install_full_redirect_url($install_state));
+      $response = batch_process(install_redirect_url($install_state), install_full_redirect_url($install_state));
+      if ($response instanceof Response) {
+        // Save $_SESSION data from batch.
+        drupal_session_commit();
+        // Send the response.
+        $response->send();
+        exit;
+      }
     }
     // If we are in the middle of processing this batch, keep sending back
     // any output from the batch process, until the task is complete.
diff --git a/core/includes/update.inc b/core/includes/update.inc
index 5dc0d5fee11b36a87aff6e5cf05d37a79dd73fb7..7ccda5adeb975fc11944ed4392af401285368c32 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -830,7 +830,7 @@ function update_do_one($module, $number, $dependency_map, &$context) {
  *
  * @see update_resolve_dependencies()
  */
-function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $redirect_callback = 'drupal_goto') {
+function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $redirect_callback = NULL) {
   // During the update, bring the site offline so that schema changes do not
   // affect visiting users.
   $maintenance_mode = config('system.maintenance')->get('enabled');
@@ -883,7 +883,7 @@ function update_batch($start, $redirect = NULL, $url = NULL, $batch = array(), $
     'file' => 'core/includes/update.inc',
   );
   batch_set($batch);
-  batch_process($redirect, $url, $redirect_callback);
+  return batch_process($redirect, $url, $redirect_callback);
 }
 
 /**
diff --git a/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php
new file mode 100644
index 0000000000000000000000000000000000000000..9da318eb867195cd97436190da6af95fd4eb2247
--- /dev/null
+++ b/core/lib/Drupal/Core/EventSubscriber/RedirectResponseSubscriber.php
@@ -0,0 +1,81 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\EventSubscriber\RedirectResponseSubscriber.
+ */
+
+namespace Drupal\Core\EventSubscriber;
+
+use Drupal\Core\Routing\PathBasedGeneratorInterface;
+use Symfony\Component\HttpKernel\KernelEvents;
+use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
+use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Access subscriber for controller requests.
+ */
+class RedirectResponseSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The url generator service.
+   *
+   * @var \Drupal\Core\Routing\PathBasedGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * Constructs a RedirectResponseSubscriber object.
+   *
+   * @param \Drupal\Core\Routing\PathBasedGeneratorInterface $url_generator
+   *   The url generator service.
+   */
+  public function __construct(PathBasedGeneratorInterface $url_generator) {
+    $this->urlGenerator = $url_generator;
+  }
+
+  /**
+   * Allows manipulation of the response object when performing a redirect.
+   *
+   * @param Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
+   *   The Event to process.
+   */
+  public function checkRedirectUrl(FilterResponseEvent $event) {
+    $response = $event->getResponse();
+    if ($response instanceOf RedirectResponse) {
+      $options = array();
+
+      $redirect_path = $response->getTargetUrl();
+      $destination = $event->getRequest()->query->get('destination');
+      // A destination in $_GET always overrides the current RedirectResponse.
+      // We do not allow absolute URLs to be passed via $_GET, as this can be an
+      // attack vector, with the following exception:
+      // - Absolute URLs that point to this site (i.e. same base URL and
+      //   base path) are allowed.
+      if ($destination && (!url_is_external($destination) || _external_url_is_local($destination))) {
+        $destination = drupal_parse_url($destination);
+
+        $path = $destination['path'];
+        $options['query'] = $destination['query'];
+        $options['fragment'] = $destination['fragment'];
+        // The 'Location' HTTP header must always be absolute.
+        $options['absolute'] = TRUE;
+
+        $response->setTargetUrl($this->urlGenerator->generateFromPath($path, $options));
+      }
+    }
+  }
+
+  /**
+   * Registers the methods in this class that should be listeners.
+   *
+   * @return array
+   *   An array of event listener definitions.
+   */
+  static function getSubscribedEvents() {
+    $events[KernelEvents::RESPONSE][] = array('checkRedirectUrl');
+    return $events;
+  }
+}
diff --git a/core/modules/action/lib/Drupal/action/Plugin/Action/GotoAction.php b/core/modules/action/lib/Drupal/action/Plugin/Action/GotoAction.php
index aff20b07f119acc5d38622baa3a58f84c28db5e2..c20801a3bd1801a141ee8280cf9c53b9c589df68 100644
--- a/core/modules/action/lib/Drupal/action/Plugin/Action/GotoAction.php
+++ b/core/modules/action/lib/Drupal/action/Plugin/Action/GotoAction.php
@@ -10,6 +10,11 @@
 use Drupal\Core\Annotation\Action;
 use Drupal\Core\Annotation\Translation;
 use Drupal\Core\Action\ConfigurableActionBase;
+use Drupal\Core\Routing\PathBasedGeneratorInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\EventDispatcher\EventDispatcherInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
+use Symfony\Component\HttpKernel\KernelEvents;
 
 /**
  * Redirects to a different URL.
@@ -22,11 +27,60 @@
  */
 class GotoAction extends ConfigurableActionBase {
 
+  /**
+   * The event dispatcher service.
+   *
+   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
+   */
+  protected $dispatcher;
+
+  /**
+   * The url generator service.
+   *
+   * @var \Drupal\Core\Routing\PathBasedGeneratorInterface
+   */
+  protected $urlGenerator;
+
+  /**
+   * Constructs a new DeleteNode object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin ID for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher
+   *   The tempstore factory.
+   * @param \Drupal\Core\Routing\PathBasedGeneratorInterface $url_generator
+   *   The url generator service.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, EventDispatcherInterface $dispatcher, PathBasedGeneratorInterface $url_generator) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+
+    $this->dispatcher = $dispatcher;
+    $this->urlGenerator = $url_generator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+    return new static($configuration, $plugin_id, $plugin_definition, $container->get('event_dispatcher'), $container->get('url_generator'));
+  }
+
   /**
    * {@inheritdoc}
    */
   public function execute($object = NULL) {
-    drupal_goto($this->configuration['url']);
+    $url = $this->urlGenerator
+      ->generateFromPath($this->configuration['url'], array('absolute' => TRUE));
+    $response = new RedirectResponse($url);
+    $listener = function($event) use ($response) {
+      $event->setResponse($response);
+    };
+    // Add the listener to the event dispatcher.
+    $this->dispatcher->addListener(KernelEvents::RESPONSE, $listener);
   }
 
   /**
diff --git a/core/modules/aggregator/tests/modules/aggregator_test/aggregator_test.module b/core/modules/aggregator/tests/modules/aggregator_test/aggregator_test.module
index b18f87bd269263d2c80ddb822bb0e1c85b50d440..4427a3a6be3c70441c20c0af63a651a1236ef5d8 100644
--- a/core/modules/aggregator/tests/modules/aggregator_test/aggregator_test.module
+++ b/core/modules/aggregator/tests/modules/aggregator_test/aggregator_test.module
@@ -1,6 +1,7 @@
 <?php
 
 use Drupal\Component\Utility\Crypt;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Implements hook_menu().
@@ -70,5 +71,5 @@ function aggregator_test_feed($use_last_modified = FALSE, $use_etag = FALSE) {
  * Page callback that redirects to another feed.
  */
 function aggregator_test_redirect() {
-  drupal_goto('aggregator/test-feed', array(), 301);
+  return new RedirectResponse(url('aggregator/test-feed', array('absolute' => TRUE)), 301);
 }
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index 3890a1ac79dae4860f2a8739fc87aab738a85ae4..b2a13a18e5052594ca8502027f154b7e4e1f96da 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\comment\Plugin\Core\Entity\Comment;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -238,7 +239,7 @@ function comment_multiple_delete_confirm($form, &$form_state) {
 
   if (!$comment_counter) {
     drupal_set_message(t('There do not appear to be any comments to delete, or your selected comment was deleted by another administrator.'));
-    drupal_goto('admin/content/comment');
+    return new RedirectResponse(url('admin/content/comment', array('absolute' => TRUE)));
   }
   else {
     return confirm_form($form,
diff --git a/core/modules/comment/comment.pages.inc b/core/modules/comment/comment.pages.inc
index 5e1b190216324bb094ed8b316fd8bffb7699cd9a..2fbcb58eb62b20867a11b63a52c840518c668756 100644
--- a/core/modules/comment/comment.pages.inc
+++ b/core/modules/comment/comment.pages.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\comment\Plugin\Core\Entity\Comment;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
@@ -47,7 +48,7 @@ function comment_reply(EntityInterface $node, $pid = NULL) {
     }
     else {
       drupal_set_message(t('You are not authorized to post comments.'), 'error');
-      drupal_goto("node/$node->nid");
+      return new RedirectResponse(url("node/$node->nid", array('absolute' => TRUE)));
     }
   }
   else {
@@ -62,19 +63,19 @@ function comment_reply(EntityInterface $node, $pid = NULL) {
           if ($comment->nid->target_id != $node->nid) {
             // Attempting to reply to a comment not belonging to the current nid.
             drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
-            drupal_goto("node/$node->nid");
+            return new RedirectResponse(url("node/$node->nid", array('absolute' => TRUE)));
           }
           // Display the parent comment
           $build['comment_parent'] = comment_view($comment);
         }
         else {
           drupal_set_message(t('The comment you are replying to does not exist.'), 'error');
-          drupal_goto("node/$node->nid");
+          return new RedirectResponse(url("node/$node->nid", array('absolute' => TRUE)));
         }
       }
       else {
         drupal_set_message(t('You are not authorized to view comments.'), 'error');
-        drupal_goto("node/$node->nid");
+        return new RedirectResponse(url("node/$node->nid", array('absolute' => TRUE)));
       }
     }
     // This is the case where the comment is in response to a node. Display the node.
@@ -85,14 +86,14 @@ function comment_reply(EntityInterface $node, $pid = NULL) {
     // Should we show the reply box?
     if ($node->comment != COMMENT_NODE_OPEN) {
       drupal_set_message(t("This discussion is closed: you can't post new comments."), 'error');
-      drupal_goto("node/$node->nid");
+      return new RedirectResponse(url("node/$node->nid", array('absolute' => TRUE)));
     }
     elseif (user_access('post comments')) {
       $build['comment_form'] = comment_add($node, $pid);
     }
     else {
       drupal_set_message(t('You are not authorized to post comments.'), 'error');
-      drupal_goto("node/$node->nid");
+      return new RedirectResponse(url("node/$node->nid", array('absolute' => TRUE)));
     }
   }
 
@@ -120,5 +121,5 @@ function comment_approve(Comment $comment) {
   $comment->save();
 
   drupal_set_message(t('Comment approved.'));
-  drupal_goto('node/' . $comment->nid->target_id);
+  return new RedirectResponse('node/' . $comment->nid->target_id, array('absolute' => TRUE));
 }
diff --git a/core/modules/image/image.admin.inc b/core/modules/image/image.admin.inc
index 59cc1c6c9e21e9962e3ab5261729f1cb2bf61d78..935df53437774c08856ab14aaf723c6c74d5c72b 100644
--- a/core/modules/image/image.admin.inc
+++ b/core/modules/image/image.admin.inc
@@ -5,6 +5,8 @@
  * Administration pages for image settings.
  */
 
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
 /**
  * Menu callback; Listing of all current image styles.
  */
@@ -284,7 +286,7 @@ function image_effect_form($form, &$form_state, $style, $effect) {
   // If there's no configuration for this image effect, return to
   // the image style page.
   if (!isset($effect['form callback'])) {
-    drupal_goto('admin/config/media/image-styles/manage/' . $style->id());
+    return new RedirectResponse(url('admin/config/media/image-styles/manage/' . $style->id(), array('absolute' => TRUE)));
   }
   $form_state['image_style'] = $style;
   $form_state['image_effect'] = $effect;
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index e41f6bccd646675e0a01c22f2443e598a9fdf097..ee3dbd19582a0942d20d48ef50cc06206236faf0 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -7,6 +7,7 @@
 
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageManager;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -298,7 +299,7 @@ function language_admin_delete_form($form, &$form_state, $language) {
 
   if (language_default()->langcode == $langcode) {
     drupal_set_message(t('The default language cannot be deleted.'));
-    drupal_goto('admin/config/regional/language');
+    return new RedirectResponse(url('admin/config/regional/language', array('absolute' => TRUE)));
   }
 
   // For other languages, warn the user that data loss is ahead.
diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc
index 47ad13172deb8e2449f5368b39baa73c2d5eda24..3400a3ef4e5f57ba610215b3233d4bba7cb68d7b 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -8,6 +8,7 @@
 use Drupal\Core\Language\Language;
 use Drupal\locale\SourceString;
 use Drupal\locale\TranslationString;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
@@ -479,9 +480,9 @@ function locale_translation_manual_status() {
   // Execute a batch if required. A batch is only used when remote files
   // are checked.
   if (batch_get()) {
-    batch_process('admin/reports/translations');
+    return batch_process('admin/reports/translations');
   }
-  drupal_goto('admin/reports/translations');
+  return new RedirectResponse(url('admin/reports/translations', array('absolute' => TRUE)));
 }
 
 /**
diff --git a/core/modules/node/lib/Drupal/node/Form/DeleteMultiple.php b/core/modules/node/lib/Drupal/node/Form/DeleteMultiple.php
index 3cb38bf008e27474b2ac4b6803d0d1f9c4144176..7a43181f49816b347103c25ab6a68383510d96cb 100644
--- a/core/modules/node/lib/Drupal/node/Form/DeleteMultiple.php
+++ b/core/modules/node/lib/Drupal/node/Form/DeleteMultiple.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Entity\EntityManager;
 use Drupal\Component\Utility\String;
 use Drupal\user\TempStoreFactory;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -97,7 +98,7 @@ public function getConfirmText() {
   public function buildForm(array $form, array &$form_state) {
     $this->nodes = $this->tempStoreFactory->get('node_multiple_delete_confirm')->get($GLOBALS['user']->uid);
     if (empty($this->nodes)) {
-      drupal_goto($this->getCancelPath());
+      return new RedirectResponse(url($this->getCancelPath(), array('absolute' => TRUE)));
     }
 
     $form['nodes'] = array(
diff --git a/core/modules/node/node.pages.inc b/core/modules/node/node.pages.inc
index f81210e97c319f1b60e67b6510a9351a2678a2f1..a731f404d7e3537c01f1bc3ae34ee1729f59befa 100644
--- a/core/modules/node/node.pages.inc
+++ b/core/modules/node/node.pages.inc
@@ -10,6 +10,7 @@
  */
 
 use Drupal\Core\Entity\EntityInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Page callback: Displays add content links for available content types.
@@ -34,7 +35,7 @@ function node_add_page() {
   // Bypass the node/add listing if only one content type is available.
   if (count($content) == 1) {
     $type = array_shift($content);
-    drupal_goto('node/add/' . $type->type);
+    return new RedirectResponse(url('node/add/' . $type->type, array('absolute' => TRUE)));
   }
   return array('#theme' => 'node_add_list', '#content' => $content);
 }
diff --git a/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php b/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php
index b6c380ef6d86a5bbf8328cc22554430e79d82bfe..18d90866d54726e4c63277df4fbd4659ce541fd9 100644
--- a/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php
+++ b/core/modules/overlay/lib/Drupal/overlay/EventSubscriber/OverlaySubscriber.php
@@ -8,6 +8,7 @@
 namespace Drupal\overlay\EventSubscriber;
 
 use Drupal\Core\ContentNegotiation;
+use Drupal\Core\Routing\PathBasedGeneratorInterface;
 use Drupal\user\UserData;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpFoundation\RedirectResponse;
@@ -34,6 +35,13 @@ class OverlaySubscriber implements EventSubscriberInterface {
    */
   protected $userData;
 
+  /**
+   * The url generator service.
+   *
+   * @var \Drupal\Core\Routing\PathBasedGeneratorInterface
+   */
+  protected $urlGenerator;
+
   /**
    * Constructs an OverlaySubscriber object.
    *
@@ -41,10 +49,13 @@ class OverlaySubscriber implements EventSubscriberInterface {
    *   The content negotiation service.
    * @param \Drupal\user\UserData $user_data
    *   The user.data service.
+   * @param \Drupal\Core\Routing\PathBasedGeneratorInterface $url_generator
+   *   The url generator service.
    */
-  public function __construct(ContentNegotiation $negotiation, UserData $user_data) {
+  public function __construct(ContentNegotiation $negotiation, UserData $user_data, PathBasedGeneratorInterface $url_generator) {
     $this->negotiation = $negotiation;
     $this->userData = $user_data;
+    $this->urlGenerator = $url_generator;
   }
 
   /**
@@ -75,7 +86,12 @@ public function onRequest(GetResponseEvent $event) {
       // <front>#overlay=admin/modules to actually enable the overlay.
       if (isset($_SESSION['overlay_enable_redirect']) && $_SESSION['overlay_enable_redirect']) {
         unset($_SESSION['overlay_enable_redirect']);
-        $response = new RedirectResponse(url('<front>', array('fragment' => 'overlay=' . $current_path, 'absolute' => TRUE)));
+        $url = $this->urlGenerator
+          ->generateFromPath('<front>', array(
+            'fragment' => 'overlay=' . $current_path,
+            'absolute' => TRUE,
+          ));
+        $response = new RedirectResponse($url);
         $event->setResponse($response);
       }
 
@@ -134,6 +150,32 @@ public function onResponse(FilterResponseEvent $event) {
           }
         }
       }
+      $response = $event->getResponse();
+      if ($response instanceOf RedirectResponse) {
+        $path = $response->getTargetUrl();
+        // The authorize.php script bootstraps Drupal to a very low level, where
+        // the PHP code that is necessary to close the overlay properly will not
+        // be loaded. Therefore, if we are redirecting to authorize.php inside
+        // the overlay, instead redirect back to the current page with
+        // instructions to close the overlay there before redirecting to the
+        // final destination.
+        $options = array('absolute' => TRUE);
+        if ($path == system_authorized_get_url($options) || $path == system_authorized_batch_processing_url($options)) {
+          $_SESSION['overlay_close_dialog'] = array($path, $options);
+          $path = current_path();
+          $options = drupal_get_query_parameters();
+        }
+
+        // If the current page request is inside the overlay, add ?render=overlay
+        // to the new path, so that it appears correctly inside the overlay.
+        if (isset($options['query'])) {
+          $options['query'] += array('render' => 'overlay');
+        }
+        else {
+          $options['query'] = array('render' => 'overlay');
+        }
+        $response->setTargetUrl($this->urlGenerator->generateFromPath($path, $options));
+      }
     }
   }
 
diff --git a/core/modules/overlay/overlay.module b/core/modules/overlay/overlay.module
index 103a8101aa299bdc5e0857826ca3e77bde395a3c..180ea972fde2baa57237e682f43ca7d24e4be4e1 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -5,6 +5,7 @@
  * Displays the Drupal administration interface in an overlay.
  */
 
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\block\Plugin\Core\Entity\Block;
@@ -176,34 +177,6 @@ function overlay_library_info() {
   return $libraries;
 }
 
-/**
- * Implements hook_drupal_goto_alter().
- */
-function overlay_drupal_goto_alter(&$path, &$options, &$http_response_code) {
-  if (overlay_get_mode() == 'child') {
-    // The authorize.php script bootstraps Drupal to a very low level, where
-    // the PHP code that is necessary to close the overlay properly will not be
-    // loaded. Therefore, if we are redirecting to authorize.php inside the
-    // overlay, instead redirect back to the current page with instructions to
-    // close the overlay there before redirecting to the final destination; see
-    // overlay_init().
-    if ($path == system_authorized_get_url() || $path == system_authorized_batch_processing_url()) {
-      $_SESSION['overlay_close_dialog'] = array($path, $options);
-      $path = current_path();
-      $options = drupal_get_query_parameters();
-    }
-
-    // If the current page request is inside the overlay, add ?render=overlay
-    // to the new path, so that it appears correctly inside the overlay.
-    if (isset($options['query'])) {
-      $options['query'] += array('render' => 'overlay');
-    }
-    else {
-      $options['query'] = array('render' => 'overlay');
-    }
-  }
-}
-
 /**
  * Implements hook_batch_alter().
  *
@@ -293,7 +266,7 @@ function overlay_user_dismiss_message() {
   Drupal::service('user.data')->set('overlay', $user->uid, 'message_dismissed', 1);
   drupal_set_message(t('The message has been dismissed. You can change your overlay settings at any time by visiting your profile page.'));
   // Destination is normally given. Go to the user profile as a fallback.
-  drupal_goto('user/' . $user->uid . '/edit');
+  return new RedirectResponse(url('user/' . $user->uid . '/edit', array('absolute' => TRUE)));
 }
 
 /**
diff --git a/core/modules/overlay/overlay.services.yml b/core/modules/overlay/overlay.services.yml
index 7ab943f78580702edde49b27ea3e9316749b9504..b15e6cda7ed39d143425570033acd52c9c99bc1a 100644
--- a/core/modules/overlay/overlay.services.yml
+++ b/core/modules/overlay/overlay.services.yml
@@ -3,4 +3,4 @@ services:
     class: Drupal\overlay\EventSubscriber\OverlaySubscriber
     tags:
       - { name: event_subscriber }
-    arguments: ['@content_negotiation', '@user.data']
+    arguments: ['@content_negotiation', '@user.data', '@url_generator']
diff --git a/core/modules/search/search.pages.inc b/core/modules/search/search.pages.inc
index aed41f625a8da85c450b18783c1b974dfa2b2db7..646028a853f01a447e160c0466d667ecd4faddfa 100644
--- a/core/modules/search/search.pages.inc
+++ b/core/modules/search/search.pages.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Page callback: Presents the search form and/or search results.
@@ -41,7 +42,7 @@ function search_view($module = NULL, $keys = '') {
     if ($keys) {
       $path .= '/' . $keys;
     }
-    drupal_goto($path);
+    return new RedirectResponse(url($path, array('absolute' => TRUE)));
   }
 
   // Default results output is an empty string.
diff --git a/core/modules/shortcut/shortcut.admin.inc b/core/modules/shortcut/shortcut.admin.inc
index 60dab601009031b4d17c3e8ad3624738a31e5e42..0671b2a5dc83ad4b346094fbc02ed1dc46ebeae3 100644
--- a/core/modules/shortcut/shortcut.admin.inc
+++ b/core/modules/shortcut/shortcut.admin.inc
@@ -5,6 +5,7 @@
  * Administrative page callbacks for the shortcut module.
  */
 
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 
 /**
@@ -498,7 +499,7 @@ function shortcut_link_add_inline($shortcut_set) {
     else {
       drupal_set_message(t('Unable to add a shortcut for %title.', array('%title' => $link['link_title'])));
     }
-    drupal_goto();
+    return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
   }
 
   throw new AccessDeniedHttpException();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestResultsForm.php b/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestResultsForm.php
index 50ee95958557e5fa81a6ddee7999ce0ec88d6dfe..35c0ce6f8195417c670fda30322c7e9086fde20b 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestResultsForm.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/Form/SimpletestResultsForm.php
@@ -11,6 +11,7 @@
 use Drupal\Core\Database\Connection;
 use Drupal\Core\Form\FormInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * Test results form for $test_id.
@@ -91,9 +92,7 @@ public function buildForm(array $form, array &$form_state, $test_id = NULL) {
 
     if (is_numeric($test_id) && !$results = $this->getResults($test_id)) {
       drupal_set_message(t('No test results to display.'), 'error');
-      drupal_goto('admin/config/development/testing');
-
-      return $form;
+      return new RedirectResponse(url('admin/config/development/testing', array('absolute' => TRUE)));
     }
 
     // Load all classes and include CSS.
diff --git a/core/modules/system/lib/Drupal/system/Tests/Common/GotoTest.php b/core/modules/system/lib/Drupal/system/Tests/Common/GotoTest.php
deleted file mode 100644
index c9e3cfc7f4737444a043c8af0daa162d52a24fc4..0000000000000000000000000000000000000000
--- a/core/modules/system/lib/Drupal/system/Tests/Common/GotoTest.php
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-
-/**
- * @file
- * Definition of Drupal\system\Tests\Common\GotoTest.
- */
-
-namespace Drupal\system\Tests\Common;
-
-use Drupal\simpletest\WebTestBase;
-
-/**
- * Tests drupal_goto() and hook_drupal_goto_alter().
- */
-class GotoTest extends WebTestBase {
-
-  /**
-   * Modules to enable.
-   *
-   * @var array
-   */
-  public static $modules = array('common_test');
-
-  public static function getInfo() {
-    return array(
-      'name' => 'Redirect functionality',
-      'description' => 'Tests the drupal_goto() and hook_drupal_goto_alter() functionality.',
-      'group' => 'Common',
-    );
-  }
-
-  /**
-   * Tests drupal_goto().
-   */
-  function testDrupalGoto() {
-    $this->drupalGet('common-test/drupal_goto/redirect');
-    $headers = $this->drupalGetHeaders(TRUE);
-    list(, $status) = explode(' ', $headers[0][':status'], 3);
-    $this->assertEqual($status, 302, 'Expected response code was sent.');
-    $this->assertText('drupal_goto', 'Drupal goto redirect succeeded.');
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('absolute' => TRUE)), 'Drupal goto redirected to expected URL.');
-
-    $this->drupalGet('common-test/drupal_goto/redirect_advanced');
-    $headers = $this->drupalGetHeaders(TRUE);
-    list(, $status) = explode(' ', $headers[0][':status'], 3);
-    $this->assertEqual($status, 301, 'Expected response code was sent.');
-    $this->assertText('drupal_goto', 'Drupal goto redirect succeeded.');
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto', array('query' => array('foo' => '123'), 'absolute' => TRUE)), 'Drupal goto redirected to expected URL.');
-
-    // Test that drupal_goto() respects ?destination=xxx. Use a complicated URL
-    // to test that the path is encoded and decoded properly.
-    $destination = 'common-test/drupal_goto/destination?foo=%2525&bar=123';
-    $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
-    $this->assertText('drupal_goto', 'Drupal goto redirect with destination succeeded.');
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/destination', array('query' => array('foo' => '%25', 'bar' => '123'), 'absolute' => TRUE)), 'Drupal goto redirected to given query string destination.');
-
-    // Test that drupal_goto() respects ?destination=xxx with an absolute URL
-    // that points to this Drupal installation.
-    $destination = url('common-test/drupal_goto/alternative', array('absolute' => TRUE));
-    $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
-    $this->assertText('drupal_goto_alternative', 'Drupal goto redirect with absolute URL destination that points to this Drupal installation succeeded.');
-    $this->assertEqual($this->getUrl(), url('common-test/drupal_goto/alternative', array('absolute' => TRUE)), 'Drupal goto redirected to given query string destination with absolute URL that points to this Drupal installation.');
-
-    // Test that drupal_goto() fails to respect ?destination=xxx with an absolute URL
-    // that does not point to this Drupal installation.
-    $destination = 'http://example.com';
-    $this->drupalGet('common-test/drupal_goto/redirect', array('query' => array('destination' => $destination)));
-    $this->assertText('drupal_goto', 'Drupal goto fails to redirect with absolute URL destination that does not point to this Drupal installation.');
-    $this->assertNotEqual($this->getUrl(), $destination, 'Drupal goto failed to redirect to given query string destination with absolute URL that does not point to this Drupal installation.');
-  }
-
-  /**
-   * Tests hook_drupal_goto_alter().
-   */
-  function testDrupalGotoAlter() {
-    $this->drupalGet('common-test/drupal_goto/redirect_fail');
-
-    $this->assertNoText(t("Drupal goto failed to stop program"), 'Drupal goto stopped program.');
-    $this->assertNoText('drupal_goto_fail', 'Drupal goto redirect failed.');
-  }
-
-  /**
-   * Tests drupal_get_destination().
-   */
-  function testDrupalGetDestination() {
-    $query = $this->randomName(10);
-
-    // Verify that a 'destination' query string is used as destination.
-    $this->drupalGet('common-test/destination', array('query' => array('destination' => $query)));
-    $this->assertText('The destination: ' . $query, 'The given query string destination is determined as destination.');
-
-    // Verify that the current path is used as destination.
-    $this->drupalGet('common-test/destination', array('query' => array($query => NULL)));
-    $url = 'common-test/destination?' . $query;
-    $this->assertText('The destination: ' . $url, 'The current path is determined as destination.');
-  }
-}
diff --git a/core/modules/system/system.admin.inc b/core/modules/system/system.admin.inc
index 583dc986d56b6342c92689e554b905e64d85659e..c49969276ea9a6c9705687ced82e3ca07df56f86 100644
--- a/core/modules/system/system.admin.inc
+++ b/core/modules/system/system.admin.inc
@@ -8,6 +8,7 @@
 use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Ajax\ReplaceCommand;
 use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Drupal\Core\Datetime\DrupalDateTime;
@@ -299,7 +300,7 @@ function system_theme_default() {
     else {
       drupal_set_message(t('The %theme theme was not found.', array('%theme' => $theme)), 'error');
     }
-    drupal_goto('admin/appearance');
+    return new RedirectResponse(url('admin/appearance', array('absolute' => TRUE)));
   }
   throw new AccessDeniedHttpException();
 }
@@ -1223,7 +1224,7 @@ function system_modules_uninstall_validate($form, &$form_state) {
   // Form submitted, but no modules selected.
   if (!count(array_filter($form_state['values']['uninstall']))) {
     drupal_set_message(t('No modules selected.'), 'error');
-    drupal_goto('admin/modules/uninstall');
+    return new RedirectResponse(url('admin/modules/uninstall', array('absolute' => TRUE)));
   }
 }
 
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 943e99a8433020b998384f7fd8445d34742ce947..e9e69c50253dc4680a2babbf4ca280290737407e 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -3003,22 +3003,6 @@ function hook_install_tasks(&$install_state) {
   return $tasks;
 }
 
-/**
- * Change the page the user is sent to by drupal_goto().
- *
- * @param $path
- *   A Drupal path or a full URL.
- * @param $options
- *   An associative array of additional URL options to pass to url().
- * @param $http_response_code
- *   The HTTP status code to use for the redirection. See drupal_goto() for more
- *   information.
- */
-function hook_drupal_goto_alter(&$path, &$options, &$http_response_code) {
-  // A good addition to misery module.
-  $http_response_code = 500;
-}
-
 /**
  * Alter XHTML HEAD tags before they are rendered by drupal_get_html_head().
  *
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index ea58a629f7191e0bf8b00d722fdd87c3b6fed620..abc9f9c1e532c1c4c4273fd967c2246a9ef1e0aa 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -12,6 +12,7 @@
 use Drupal\Core\TypedData\Primitive;
 use Drupal\system\Plugin\Block\SystemMenuBlock;
 use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Guzzle\Http\Exception\BadResponseException;
 use Guzzle\Http\Exception\RequestException;
@@ -2347,7 +2348,7 @@ function _system_themes_access($theme) {
  * or to call system_authorized_init() and then redirect to authorize.php,
  * using the URL from system_authorized_get_url(). Redirecting yourself is
  * necessary when your authorized operation is being triggered by a form
- * submit handler, since calling drupal_goto() in a submit handler is a bad
+ * submit handler, since calling redirecting in a submit handler is a bad
  * idea, and you should instead set $form_state['redirect'].
  *
  * Once the SESSION is setup for the operation and the user is redirected to
@@ -2375,7 +2376,7 @@ function _system_themes_access($theme) {
  * not to assume any code exists. Example (system_authorized_run()):
  * @code
  *   system_authorized_init($callback, $file, $arguments, $page_title);
- *   drupal_goto(system_authorized_get_url());
+ *   return new RedirectResponse(system_authorized_get_url());
  * @endcode
  * Example (update_manager_install_form_submit()):
  * @code
@@ -2436,9 +2437,13 @@ function system_authorized_get_url(array $options = array()) {
 
 /**
  * Returns the URL for the authorize.php script when it is processing a batch.
+ *
+ * @param array $options
+ *   Optional array of options to pass to url().
  */
-function system_authorized_batch_processing_url() {
-  return system_authorized_get_url(array('query' => array('batch' => '1')));
+function system_authorized_batch_processing_url(array $options = array()) {
+  $options['query'] = array('batch' => '1');
+  return system_authorized_get_url($options);
 }
 
 /**
@@ -2448,7 +2453,7 @@ function system_authorized_batch_processing_url() {
  */
 function system_authorized_run($callback, $file, $arguments = array(), $page_title = NULL) {
   system_authorized_init($callback, $file, $arguments, $page_title);
-  drupal_goto(system_authorized_get_url());
+  return new RedirectResponse(system_authorized_get_url());
 }
 
 /**
@@ -2459,7 +2464,7 @@ function system_authorized_run($callback, $file, $arguments = array(), $page_tit
 function system_authorized_batch_process() {
   $finish_url = system_authorized_get_url();
   $process_url = system_authorized_batch_processing_url();
-  batch_process($finish_url, $process_url);
+  return batch_process($finish_url, $process_url);
 }
 
 /**
@@ -3370,7 +3375,7 @@ function system_admin_compact_mode() {
  */
 function system_admin_compact_page($mode = 'off') {
   user_cookie_save(array('admin_compact_mode' => ($mode == 'on')));
-  drupal_goto();
+  return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
 }
 
 /**
diff --git a/core/modules/system/tests/modules/batch_test/batch_test.module b/core/modules/system/tests/modules/batch_test/batch_test.module
index 85ee47c0a844b7e2243bab0b796fe939405e924f..2c1bee2cfe1d88c9c5062d07587cf11d80f66bc7 100644
--- a/core/modules/system/tests/modules/batch_test/batch_test.module
+++ b/core/modules/system/tests/modules/batch_test/batch_test.module
@@ -294,7 +294,7 @@ function batch_test_nested_drupal_form_submit($value = 1) {
     array('_batch_test_nested_drupal_form_submit_callback', array($value)),
   );
   batch_set($batch);
-  batch_process('batch-test/redirect');
+  return batch_process('batch-test/redirect');
 }
 
 /**
@@ -338,7 +338,7 @@ function batch_test_no_form() {
   batch_test_stack(NULL, TRUE);
 
   batch_set(_batch_test_batch_1());
-  batch_process('batch-test/redirect');
+  return batch_process('batch-test/redirect');
 }
 
 /**
@@ -348,7 +348,7 @@ function batch_test_large_percentage() {
   batch_test_stack(NULL, TRUE);
 
   batch_set(_batch_test_batch_5());
-  batch_process('batch-test/redirect');
+  return batch_process('batch-test/redirect');
 }
 
 /**
@@ -506,7 +506,7 @@ function batch_test_theme_batch() {
     ),
   );
   batch_set($batch);
-  batch_process('batch-test/redirect');
+  return batch_process('batch-test/redirect');
 }
 
 /**
diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module
index 0334219484c09f90b248d2d003e7214a404defc8..83d19e0309a92bbe7dbb627c6b2c474498ac7434 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -9,43 +9,6 @@
  * Implements hook_menu().
  */
 function common_test_menu() {
-  $items['common-test/drupal_goto'] = array(
-    'title' => 'Drupal Goto',
-    'page callback' => 'common_test_drupal_goto_land',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  $items['common-test/drupal_goto/alternative'] = array(
-    'title' => 'Drupal Goto',
-    'page callback' => 'common_test_drupal_goto_land_alternative',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  $items['common-test/drupal_goto/fail'] = array(
-    'title' => 'Drupal Goto',
-    'page callback' => 'common_test_drupal_goto_land_fail',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  $items['common-test/drupal_goto/redirect'] = array(
-    'title' => 'Drupal Goto',
-    'page callback' => 'common_test_drupal_goto_redirect',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  $items['common-test/drupal_goto/redirect_advanced'] = array(
-    'title' => 'Drupal Goto',
-    'page callback' => 'common_test_drupal_goto_redirect_advanced',
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
-  $items['common-test/drupal_goto/redirect_fail'] = array(
-    'title' => 'Drupal Goto Failure',
-    'page callback' => 'drupal_goto',
-    'page arguments' => array('common-test/drupal_goto/fail'),
-    'access arguments' => array('access content'),
-    'type' => MENU_CALLBACK,
-  );
   $items['common-test/destination'] = array(
     'title' => 'Drupal Get Destination',
     'page callback' => 'common_test_destination',
@@ -67,56 +30,6 @@ function common_test_menu() {
   return $items;
 }
 
-/**
- * Redirects using drupal_goto().
- */
-function common_test_drupal_goto_redirect() {
-  drupal_goto('common-test/drupal_goto');
-}
-
-/**
- * Redirects using drupal_goto().
- */
-function common_test_drupal_goto_redirect_advanced() {
-  drupal_goto('common-test/drupal_goto', array('query' => array('foo' => '123')), 301);
-}
-
-/**
- * Page callback: Provides a landing page for drupal_goto().
- *
- * @see common_test_menu()
- */
-function common_test_drupal_goto_land() {
-  print "drupal_goto";
-}
-
-/**
- * Page callback: Provides a landing page for drupal_goto().
- *
- * @see common_test_menu()
- */
-function common_test_drupal_goto_land_alternative() {
-  print "drupal_goto_alternative";
-}
-
-/**
- * Page callback: Provides a failure landing page for drupal_goto().
- *
- * @see common_test_menu()
- */
-function common_test_drupal_goto_land_fail() {
-  print "drupal_goto_fail";
-}
-
-/**
- * Implements hook_drupal_goto_alter().
- */
-function common_test_drupal_goto_alter(&$path, &$options, &$http_response_code) {
-  if ($path == 'common-test/drupal_goto/fail') {
-    $path = 'common-test/drupal_goto/redirect';
-  }
-}
-
 /**
  * Prints a destination query parameter.
  */
diff --git a/core/modules/system/tests/modules/form_test/form_test.module b/core/modules/system/tests/modules/form_test/form_test.module
index 9c3691648f5b57b8a57d21ba20f692586d0b9263..25ae931c158f0917f2a18834bc0b35bd9b8f42c3 100644
--- a/core/modules/system/tests/modules/form_test/form_test.module
+++ b/core/modules/system/tests/modules/form_test/form_test.module
@@ -387,6 +387,7 @@ function _form_test_submit_values_json($form, &$form_state) {
   $response = new JsonResponse($form_state['values']);
   // @todo remove once converted to new routing system.
   $response->send();
+  exit;
 }
 
 /**
diff --git a/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php b/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php
index c9f1b0abced15e4a75e32b74a6abc2bc57825e06..45234a8e92d80540b0688e7d1d4cd896d2420034 100644
--- a/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php
+++ b/core/modules/system/tests/modules/session_test/lib/Drupal/session_test/EventSubscriber/SessionTestSubscriber.php
@@ -7,10 +7,11 @@
 
 namespace Drupal\session_test\EventSubscriber;
 
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
  * Defines a test session subscriber that checks whether the session is empty.
@@ -33,13 +34,27 @@ public function onKernelRequestSessionTest(GetResponseEvent $event) {
   }
 
   /**
-   * Set header for session testing.
+   * Performs tasks for session_test module on kernel.response.
    *
    * @param \Symfony\Component\HttpKernel\Event\FilterResponseEvent $event
    *   The Event to process.
    */
   public function onKernelResponseSessionTest(FilterResponseEvent $event) {
-    $event->getResponse()->headers->set('X-Session-Empty', $this->emptySession);
+    $response = $event->getResponse();
+    if ($response instanceOf RedirectResponse) {
+      // Force the redirection to go to a non-secure page after being on a
+      // secure page through https.php.
+      global $base_insecure_url, $is_https_mock;
+      // Alter the redirect to use HTTP when using a mock HTTPS request through
+      // https.php because form submissions would otherwise redirect to a
+      // non-existent HTTPS site.
+      if (!empty($is_https_mock)) {
+        $path = $base_insecure_url . '/' . $event->getTargetUrl();
+        $response->setTargetUrl($path);
+      }
+    }
+    // Set header for session testing.
+    $response->headers->set('X-Session-Empty', $this->emptySession);
   }
 
   /**
diff --git a/core/modules/system/tests/modules/session_test/session_test.module b/core/modules/system/tests/modules/session_test/session_test.module
index b3e82fdb18b31e543d3ee7a7bffb88b4419127f7..73bf3ff34184e7d3622c02f4893ead16e71ab0c0 100644
--- a/core/modules/system/tests/modules/session_test/session_test.module
+++ b/core/modules/system/tests/modules/session_test/session_test.module
@@ -161,22 +161,6 @@ function session_test_form_user_login_form_alter(&$form) {
   $form['#https'] = TRUE;
 }
 
-/**
- * Implements hook_drupal_goto_alter().
- *
- * Force the redirection to go to a non-secure page after being on a secure
- * page through https.php.
- */
-function session_test_drupal_goto_alter(&$path, &$options, &$http_response_code) {
-  global $base_insecure_url, $is_https_mock;
-  // Alter the redirect to use HTTP when using a mock HTTPS request through
-  // https.php because form submissions would otherwise redirect to a
-  // non-existent HTTPS site.
-  if (!empty($is_https_mock)) {
-    $path = $base_insecure_url . '/' . $path;
-  }
-}
-
 /**
  * Menu callback, only available if current user is logged in.
  */
diff --git a/core/modules/system/tests/modules/system_test/system_test.module b/core/modules/system/tests/modules/system_test/system_test.module
index b73bb5a0ec7d0a72b4122d22ce3a86359c39f34a..ea10a04b74f1571aeff4787eba7fa8ecedc02ab0 100644
--- a/core/modules/system/tests/modules/system_test/system_test.module
+++ b/core/modules/system/tests/modules/system_test/system_test.module
@@ -1,5 +1,7 @@
 <?php
 
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
 /**
  * Implements hook_menu().
  */
@@ -276,5 +278,5 @@ function system_test_filetransfer_info() {
 function system_test_authorize_init_page($page_title) {
   $authorize_url = $GLOBALS['base_url'] . '/core/authorize.php';
   system_authorized_init('system_test_authorize_run', drupal_get_path('module', 'system_test') . '/system_test.module', array(), $page_title);
-  drupal_goto($authorize_url);
+  return new RedirectResponse($authorize_url);
 }
diff --git a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
index 906248a34c6f70b22be6254e0bcafdabb983857a..1fb68e584bf9aee6e047260c57dd09419e868873 100644
--- a/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
+++ b/core/modules/translation_entity/lib/Drupal/translation_entity/EntityTranslationController.php
@@ -469,7 +469,7 @@ public function entityFormSourceChange($form, &$form_state) {
     $entity = $form_controller->getEntity();
     $source = $form_state['values']['source_langcode']['source'];
     $path = $this->getBasePath($entity) . '/translations/add/' . $source . '/' . $form_controller->getFormLangcode($form_state);
-    $form_state['redirect'] = array('path' => $path);
+    $form_state['redirect'] = $path;
     $languages = language_list();
     drupal_set_message(t('Source language set to: %language', array('%language' => $languages[$source]->name)));
   }
diff --git a/core/modules/update/update.authorize.inc b/core/modules/update/update.authorize.inc
index c74a93fdd58fd13e8dc36dfedc83fbc7dcf0e46d..57acc3406e11d8bc52a765b0c453e64b29020325 100644
--- a/core/modules/update/update.authorize.inc
+++ b/core/modules/update/update.authorize.inc
@@ -53,7 +53,7 @@ function update_authorize_run_update($filetransfer, $projects) {
 
   batch_set($batch);
   // Invoke the batch via authorize.php.
-  system_authorized_batch_process();
+  return system_authorized_batch_process();
 }
 
 /**
@@ -98,7 +98,7 @@ function update_authorize_run_install($filetransfer, $project, $updater_name, $l
   batch_set($batch);
 
   // Invoke the batch via authorize.php.
-  system_authorized_batch_process();
+  return system_authorized_batch_process();
 }
 
 /**
diff --git a/core/modules/update/update.fetch.inc b/core/modules/update/update.fetch.inc
index 57283ea986566f52c51ae749ef9d2cda7b6cf2ed..13a13835195d2d352b95a95ab84d85b49a918bac 100644
--- a/core/modules/update/update.fetch.inc
+++ b/core/modules/update/update.fetch.inc
@@ -28,7 +28,7 @@ function update_manual_status() {
     'file' => drupal_get_path('module', 'update') . '/update.fetch.inc',
   );
   batch_set($batch);
-  batch_process('admin/reports/updates');
+  return batch_process('admin/reports/updates');
 }
 
 /**
diff --git a/core/modules/update/update.manager.inc b/core/modules/update/update.manager.inc
index bc73db0e5dfefee2e5af9f5a488a42b01c1b4ca0..8e2bce964a6a04bf4b5635247a6958757647102b 100644
--- a/core/modules/update/update.manager.inc
+++ b/core/modules/update/update.manager.inc
@@ -38,6 +38,7 @@
 
 use Drupal\Core\Updater\Updater;
 use Drupal\Core\FileTransfer\Local;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 
 /**
  * @defgroup update_manager_update Update Manager module: update
@@ -356,7 +357,7 @@ function update_manager_download_batch_finished($success, $results) {
   elseif ($success) {
     drupal_set_message(t('Updates downloaded successfully.'));
     $_SESSION['update_manager_update_projects'] = $results['projects'];
-    drupal_goto('admin/update/ready');
+    return new RedirectResponse(url('admin/update/ready', array('absolute' => TRUE)));
   }
   else {
     // Ideally we're catching all Exceptions, so they should never see this,
diff --git a/core/modules/user/lib/Drupal/user/Controller/UserController.php b/core/modules/user/lib/Drupal/user/Controller/UserController.php
index 864e09533d27f07c9787d21940d0a3cf4721b6bf..e7820194456149fdda8a01a7ff102cfaaea2e19f 100644
--- a/core/modules/user/lib/Drupal/user/Controller/UserController.php
+++ b/core/modules/user/lib/Drupal/user/Controller/UserController.php
@@ -41,9 +41,7 @@ public static function create(ContainerInterface $container) {
    */
   public function logout(Request $request) {
     user_logout();
-    // @todo Remove the destination check once drupal.org/node/1668866 is in.
-    $url = $request->query->get('destination') ?: '<front>';
-    return new RedirectResponse(url($url, array('absolute' => TRUE)));
+    return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
   }
 
 }
diff --git a/core/modules/user/lib/Drupal/user/RegisterFormController.php b/core/modules/user/lib/Drupal/user/RegisterFormController.php
index ceecd65bb7d839ee5c2d7f107aec19dd68917c3a..7d22cd3820dfa6c8e2950a8e6218eefcd5dd7144 100644
--- a/core/modules/user/lib/Drupal/user/RegisterFormController.php
+++ b/core/modules/user/lib/Drupal/user/RegisterFormController.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\user;
 
+use Symfony\Component\HttpFoundation\RedirectResponse;
+
 /**
  * Form controller for the user register forms.
  */
@@ -31,7 +33,7 @@ public function form(array $form, array &$form_state) {
 
     // If we aren't admin but already logged on, go to the user page instead.
     if (!$admin && $user->uid) {
-      drupal_goto('user/' . $user->uid);
+      return new RedirectResponse(url('user/' . $user->uid, array('absolute' => TRUE)));
     }
 
     $form['#attached']['library'][] = array('system', 'jquery.cookie');
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index e0ef6e6f8b508748d4a900bd830bfceed54d903f..bf0b052ac48e2b98f10378e9e69cfd7ea40f9924 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -12,6 +12,7 @@
 use Drupal\user\UserRole;
 use Drupal\user\RoleInterface;
 use Drupal\Core\Template\Attribute;
+use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Drupal\menu_link\Plugin\Core\Entity\MenuLink;
 
@@ -2013,7 +2014,7 @@ function user_multiple_cancel_confirm($form, &$form_state) {
     drupal_set_message($message, $redirect ? 'error' : 'warning');
     // If only user 1 was selected, redirect to the overview.
     if ($redirect) {
-      drupal_goto('admin/people');
+      return new RedirectResponse(url('admin/people', array('absolute' => TRUE)));
     }
   }
 
diff --git a/core/modules/user/user.pages.inc b/core/modules/user/user.pages.inc
index 26fb04024e0135a920d0568e5e13830104c70d7d..a8559d080308ff5a403e03bdd946c5c162a27db6 100644
--- a/core/modules/user/user.pages.inc
+++ b/core/modules/user/user.pages.inc
@@ -112,7 +112,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
         drupal_set_message(t('The one-time login link you clicked is invalid.'));
       }
     }
-    drupal_goto();
+    return new RedirectResponse(url('<front>', array('absolute' => TRUE)));
   }
   else {
     // Time out, in seconds, until login URL expires.
@@ -124,7 +124,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
       // No time out for first time login.
       if ($account->login && $current - $timestamp > $timeout) {
         drupal_set_message(t('You have tried to use a one-time login link that has expired. Please request a new one using the form below.'));
-        drupal_goto('user/password');
+        return new RedirectResponse(url('user/password', array('absolute' => TRUE)));
       }
       elseif ($account->uid && $timestamp >= $account->login && $timestamp <= $current && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) {
         // First stage is a confirmation form, then login
@@ -139,7 +139,10 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
           // Let the user's password be changed without the current password check.
           $token = Crypt::randomStringHashed(55);
           $_SESSION['pass_reset_' . $user->uid] = $token;
-          drupal_goto('user/' . $user->uid . '/edit', array('query' => array('pass-reset-token' => $token)));
+          return new RedirectResponse(url('user/' . $user->uid . '/edit', array(
+            'query' => array('pass-reset-token' => $token),
+            'absolute' => TRUE,
+          )));
         }
         else {
           if (!$account->login) {
@@ -158,7 +161,7 @@ function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $a
       }
       else {
         drupal_set_message(t('You have tried to use a one-time login link that has either been used or is no longer valid. Please request a new one using the form below.'));
-        drupal_goto('user/password');
+        return new RedirectResponse(url('user/password', array('absolute' => TRUE)));
       }
     }
     else {
@@ -389,11 +392,11 @@ function user_cancel_confirm($account, $timestamp = 0, $hashed_pass = '') {
       // Since user_cancel() is not invoked via Form API, batch processing needs
       // to be invoked manually and should redirect to the front page after
       // completion.
-      batch_process('');
+      return batch_process('');
     }
     else {
       drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'));
-      drupal_goto("user/$account->uid/cancel");
+      return new RedirectResponse(url("user/$account->uid/cancel", array('absolute' => TRUE)));
     }
   }
   throw new AccessDeniedHttpException();
diff --git a/core/update.php b/core/update.php
index a3dd4bb6dcf103d2116fda58118a8958dca4dc2e..b73e3ce26e9ecf87116ab1a1cec8fb05f578cccc 100644
--- a/core/update.php
+++ b/core/update.php
@@ -512,7 +512,7 @@ function update_check_requirements($skip_warnings = FALSE) {
         // update.php correctly by default.
         $batch_url = $base_root . drupal_current_script_url();
         $redirect_url = $base_root . drupal_current_script_url(array('op' => 'results'));
-        update_batch($request->request->get('start'), $redirect_url, $batch_url);
+        $output = update_batch($request->request->get('start'), $redirect_url, $batch_url);
         break;
       }