diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 4b995c1..ad18aec 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -196,67 +196,34 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
       unset($_SESSION['batch_form_state']);
       return $this->rebuildForm($form_id, $form_state);
     }
-
-    // If the incoming input contains a form_build_id, we'll check the cache for
-    // a copy of the form in question. If it's there, we don't have to rebuild
-    // the form to proceed. In addition, if there is stored form_state data from
-    // a previous step, we'll retrieve it so it can be passed on to the form
-    // processing code.
-    $check_cache = isset($input['form_id']) && $input['form_id'] == $form_id && !empty($input['form_build_id']);
-    if ($check_cache) {
-      $form = $this->getCache($input['form_build_id'], $form_state);
+    if (isset($input['form_id']) && $input['form_id'] == $form_id && !empty($input['form_input_steps'])) {
+      $input_steps = @json_decode($input['form_input_steps'], TRUE);
     }
-
-    // If the previous bit of code didn't result in a populated $form object, we
-    // are hitting the form for the first time and we need to build it from
-    // scratch.
-    if (!isset($form)) {
-      // If we attempted to serve the form from cache, uncacheable $form_state
-      // keys need to be removed after retrieving and preparing the form, except
-      // any that were already set prior to retrieving the form.
-      if ($check_cache) {
-        $form_state_before_retrieval = clone $form_state;
-      }
-
+    // json_decode returns NULL if it can't decode the JSON.
+    if (!isset($input_steps) || !is_array($input_steps)) {
+      $input_steps = [];
+    }
+    $input_steps[] = $input;
+    $form_state->setUserInputSteps($input_steps);
+    foreach ($input_steps as $current_input) {
+      $form_state->setRedirectUrl(NULL);
+      $form_state->setRebuild(FALSE);
+      $form_state->setUserInput($current_input);
       $form = $this->retrieveForm($form_id, $form_state);
       $this->prepareForm($form_id, $form, $form_state);
-
-      // self::setCache() removes uncacheable $form_state keys (see properties
-      // in \Drupal\Core\Form\FormState) in order for multi-step forms to work
-      // properly. This means that form processing logic for single-step forms
-      // using $form_state->isCached() may depend on data stored in those keys
-      // during self::retrieveForm()/self::prepareForm(), but form processing
-      // should not depend on whether the form is cached or not, so $form_state
-      // is adjusted to match what it would be after a
-      // self::setCache()/self::getCache() sequence. These exceptions are
-      // allowed to survive here:
-      // - always_process: Does not make sense in conjunction with form caching
-      //   in the first place, since passing form_build_id as a GET parameter is
-      //   not desired.
-      // - temporary: Any assigned data is expected to survives within the same
-      //   page request.
-      if ($check_cache) {
-        $cache_form_state = $form_state->getCacheableArray();
-        $cache_form_state['always_process'] = $form_state->getAlwaysProcess();
-        $cache_form_state['temporary'] = $form_state->getTemporary();
-        $form_state = $form_state_before_retrieval;
-        $form_state->setFormState($cache_form_state);
-      }
+      // Now that we have a constructed form, process it. This is where:
+      // - Element #process functions get called to further refine $form.
+      // - User input, if any, gets incorporated in the #value property of the
+      //   corresponding elements and into $form_state->getValues().
+      // - Validation and submission handlers are called.
+      // - If this submission is part of a multistep workflow, the form is rebuilt
+      //   to contain the information of the next step.
+      // - If necessary, the form and form state are cached or re-cached, so that
+      //   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.
+      $response = $this->processForm($form_id, $form, $form_state);
     }
-
-    // Now that we have a constructed form, process it. This is where:
-    // - Element #process functions get called to further refine $form.
-    // - User input, if any, gets incorporated in the #value property of the
-    //   corresponding elements and into $form_state->getValues().
-    // - Validation and submission handlers are called.
-    // - If this submission is part of a multistep workflow, the form is rebuilt
-    //   to contain the information of the next step.
-    // - If necessary, the form and form state are cached or re-cached, so that
-    //   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.
-    $response = $this->processForm($form_id, $form, $form_state);
-
     // If the form returns a response, skip subsequent page construction by
     // throwing an exception.
     // @see Drupal\Core\EventSubscriber\EnforcedFormResponseSubscriber
@@ -586,6 +553,17 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
       // and #parents being set elsewhere.
       '#parents' => array('form_build_id'),
     );
+    $form['form_input_steps'] = array(
+      '#type' => 'hidden',
+      '#value' => json_encode($form_state->getUserInputSteps()),
+      '#name' => 'form_input_steps',
+      // Form processing and validation requires this value, so ensure the
+      // submitted form value appears literally, regardless of custom #tree
+      // and #parents being set elsewhere.
+      '#parents' => array('form_input_steps'),
+    );
+
+
 
     // Add a token, based on either #token or form_id, to any form displayed to
     // authenticated users. This ensures that any submitted form was actually
diff --git a/core/lib/Drupal/Core/Form/FormState.php b/core/lib/Drupal/Core/Form/FormState.php
index 9d651cd..e1b3ee1 100644
--- a/core/lib/Drupal/Core/Form/FormState.php
+++ b/core/lib/Drupal/Core/Form/FormState.php
@@ -427,6 +427,11 @@ class FormState implements FormStateInterface {
   protected $submit_handlers = [];
 
   /**
+   * @var array
+   */
+  protected $userInputSteps;
+
+  /**
    * {@inheritdoc}
    */
   public function setFormState(array $form_state_additions) {
@@ -924,6 +929,21 @@ public function setUserInput(array $user_input) {
   /**
    * {@inheritdoc}
    */
+  public function setUserInputSteps(array $user_input_steps) {
+    $this->userInputSteps = $user_input_steps;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getUserInputSteps() {
+    return $this->userInputSteps;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
   public function &getValues() {
     return $this->values;
   }
@@ -1015,7 +1035,7 @@ public function setRedirect($route_name, array $route_parameters = array(), arra
   /**
    * {@inheritdoc}
    */
-  public function setRedirectUrl(Url $url) {
+  public function setRedirectUrl(Url $url = NULL) {
     $this->redirect = $url;
     return $this;
   }
diff --git a/core/lib/Drupal/Core/Form/FormStateInterface.php b/core/lib/Drupal/Core/Form/FormStateInterface.php
index 5e44ecb..6bc3fc5 100644
--- a/core/lib/Drupal/Core/Form/FormStateInterface.php
+++ b/core/lib/Drupal/Core/Form/FormStateInterface.php
@@ -146,7 +146,7 @@ public function setRedirect($route_name, array $route_parameters = array(), arra
    *
    * @see \Drupal\Core\Form\FormSubmitterInterface::redirectForm()
    */
-  public function setRedirectUrl(Url $url);
+  public function setRedirectUrl(Url $url = NULL);
 
   /**
    * Gets the value to use for redirecting after the form has been executed.
@@ -280,6 +280,9 @@ public function &getUserInput();
    */
   public function setUserInput(array $user_input);
 
+  public function setUserInputSteps(array $user_input_steps);
+  public function getUserInputSteps();
+
   /**
    * Returns the submitted and sanitized form values.
    *
