diff --git a/core/core.services.yml b/core/core.services.yml
index 093c823..d4798ac 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -282,7 +282,7 @@ services:
     arguments: ['@stream_wrapper_manager', '@settings', '@logger.channel.file']
   form_builder:
     class: Drupal\Core\Form\FormBuilder
-    arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@?csrf_token']
+    arguments: ['@form_validator', '@form_submitter', '@form_cache', '@module_handler', '@event_dispatcher', '@request_stack', '@class_resolver', '@element_info', '@theme.manager', '@string_translation', '@?csrf_token']
   form_validator:
     class: Drupal\Core\Form\FormValidator
     arguments: ['@request_stack', '@string_translation', '@csrf_token', '@logger.channel.form', '@form_error_handler']
@@ -820,7 +820,7 @@ services:
       - { name: event_subscriber }
   form_ajax_subscriber:
     class: Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber
-    arguments: ['@form_ajax_response_builder']
+    arguments: ['@form_ajax_response_builder', '@renderer']
     tags:
       - { name: event_subscriber }
   route_enhancer.lazy_collector:
diff --git a/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php b/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
index 04ec605..73fe812 100644
--- a/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
+++ b/core/lib/Drupal/Core/Form/EventSubscriber/FormAjaxSubscriber.php
@@ -7,13 +7,17 @@
 
 namespace Drupal\Core\Form\EventSubscriber;
 
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
 use Drupal\Core\Form\FormAjaxException;
 use Drupal\Core\Form\FormAjaxResponseBuilderInterface;
 use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Render\RendererInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseForControllerResultEvent;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\KernelEvents;
 
 /**
@@ -29,13 +33,23 @@ class FormAjaxSubscriber implements EventSubscriberInterface {
   protected $formAjaxResponseBuilder;
 
   /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface
+   */
+  protected $renderer;
+
+  /**
    * Constructs a new FormAjaxSubscriber.
    *
    * @param \Drupal\Core\Form\FormAjaxResponseBuilderInterface $form_ajax_response_builder
    *   The form AJAX response builder.
+   * @param \Drupal\Core\Render\RendererInterface $renderer
+   *   The renderer service.
    */
-  public function __construct(FormAjaxResponseBuilderInterface $form_ajax_response_builder) {
+  public function __construct(FormAjaxResponseBuilderInterface $form_ajax_response_builder, RendererInterface $renderer) {
     $this->formAjaxResponseBuilder = $form_ajax_response_builder;
+    $this->renderer = $renderer;
   }
 
   /**
@@ -64,9 +78,24 @@ public function onView(GetResponseForControllerResultEvent $event) {
    *   The event to process.
    */
   public function onException(GetResponseForExceptionEvent $event) {
+    $exception = $event->getException();
+    $request = $event->getRequest();
+
+    // Render a nice error message in case we have a file upload which exceeds
+    // the configured upload limit.
+    if ($exception instanceof BadRequestHttpException && $request->query->has(FormBuilderInterface::AJAX_FORM_REQUEST)) {
+      $this->drupalSetMessage($exception->getMessage(), 'error');
+      $response = new AjaxResponse();
+      $status_messages = ['#type' => 'status_messages'];
+      $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
+      $response->headers->set('X-Status-Code', 200);
+      $event->setResponse($response);
+      return;
+    }
+
     // Extract the form AJAX exception (it may have been passed to another
     // exception before reaching here).
-    if ($exception = $this->getFormAjaxException($event->getException())) {
+    if ($exception = $this->getFormAjaxException($exception)) {
       $request = $event->getRequest();
       $form = $exception->getForm();
       $form_state = $exception->getFormState();
@@ -123,4 +152,13 @@ public static function getSubscribedEvents() {
     return $events;
   }
 
+  /**
+   * Wraps drupal_set_message().
+   *
+   * @codeCoverageIgnore
+   */
+  protected function drupalSetMessage($message = NULL, $type = 'status', $repeat = FALSE) {
+    drupal_set_message($message, $type, $repeat);
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php b/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php
index 293475c..7d1c4b7 100644
--- a/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php
@@ -71,7 +71,7 @@ public function buildResponse(Request $request, array $form, FormStateInterface
     if (empty($callback) || !is_callable($callback)) {
       throw new HttpException(500, 'The specified #ajax callback is empty or not callable.');
     }
-    $result = call_user_func_array($callback, [&$form, &$form_state]);
+    $result = call_user_func_array($callback, [&$form, &$form_state, $request]);
 
     // If the callback is an #ajax callback, the result is a render array, and
     // we need to turn it into an AJAX response, so that we can add any commands
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index 0f31a1c..733618e 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -18,10 +18,13 @@
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Render\Element;
 use Drupal\Core\Render\ElementInfoManagerInterface;
+use Drupal\Core\StringTranslation\StringTranslationTrait;
+use Drupal\Core\StringTranslation\TranslationInterface;
 use Drupal\Core\Theme\ThemeManagerInterface;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\HttpFoundation\RequestStack;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 
 /**
  * Provides form building and processing.
@@ -30,6 +33,8 @@
  */
 class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormSubmitterInterface, FormCacheInterface {
 
+  use StringTranslationTrait;
+
   /**
    * The module handler.
    *
@@ -124,10 +129,12 @@ class FormBuilder implements FormBuilderInterface, FormValidatorInterface, FormS
    *   The element info manager.
    * @param \Drupal\Core\Theme\ThemeManagerInterface $theme_manager
    *   The theme manager.
+   * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation
+   *   The string translation service.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token generator.
    */
-  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ElementInfoManagerInterface $element_info, ThemeManagerInterface $theme_manager, CsrfTokenGenerator $csrf_token = NULL) {
+  public function __construct(FormValidatorInterface $form_validator, FormSubmitterInterface $form_submitter, FormCacheInterface $form_cache, ModuleHandlerInterface $module_handler, EventDispatcherInterface $event_dispatcher, RequestStack $request_stack, ClassResolverInterface $class_resolver, ElementInfoManagerInterface $element_info, ThemeManagerInterface $theme_manager, TranslationInterface $string_translation, CsrfTokenGenerator $csrf_token = NULL) {
     $this->formValidator = $form_validator;
     $this->formSubmitter = $form_submitter;
     $this->formCache = $form_cache;
@@ -138,6 +145,7 @@ public function __construct(FormValidatorInterface $form_validator, FormSubmitte
     $this->elementInfo = $element_info;
     $this->csrfToken = $csrf_token;
     $this->themeManager = $theme_manager;
+    $this->stringTranslation = $string_translation;
   }
 
   /**
@@ -264,6 +272,13 @@ public function buildForm($form_id, FormStateInterface &$form_state) {
     // can use it to know or update information about the state of the form.
     $response = $this->processForm($form_id, $form, $form_state);
 
+    // In case the post request exceeds the configured allowed size
+    // (post_max_size), the post request is potentially broken. Add some
+    // protection against that and at the same time have a nice error message.
+    if ($ajax_form_request && !isset($form_state->getUserInput()['form_id'])) {
+      throw new BadRequestHttpException($this->t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', ['@size' => $this->getFormattedFileUploadMaxSize()]));
+    }
+
     // After processing the form, if this is an AJAX form request, interrupt
     // form rendering and return by throwing an exception that contains the
     // processed form and form state. This exception will be caught by
@@ -1152,6 +1167,17 @@ protected function buttonWasClicked($element, FormStateInterface &$form_state) {
   }
 
   /**
+   * Wraps format_size() and file_upload_max_size().
+   *
+   * @return string
+   *   A translated string representation of the size of the file size limit
+   *   based on the PHP upload_max_filesize and post_max_size.
+   */
+  protected function getFormattedFileUploadMaxSize() {
+    return format_size(file_upload_max_size());
+  }
+
+  /**
    * Gets the current active user.
    *
    * @return \Drupal\Core\Session\AccountInterface
diff --git a/core/misc/ajax.js b/core/misc/ajax.js
index 2b9aea3..f8a1cb0 100644
--- a/core/misc/ajax.js
+++ b/core/misc/ajax.js
@@ -328,20 +328,6 @@
       }
       else if (this.element && element.form) {
         this.url = this.$form.attr('action');
-
-        // @todo If there's a file input on this form, then jQuery will submit
-        //   the AJAX response with a hidden Iframe rather than the XHR object.
-        //   If the response to the submission is an HTTP redirect, then the
-        //   Iframe will follow it, but the server won't content negotiate it
-        //   correctly, because there won't be an ajax_iframe_upload POST
-        //   variable. Until we figure out a work around to this problem, we
-        //   prevent AJAX-enabling elements that submit to the same URL as the
-        //   form when there's a file input. For example, this means the Delete
-        //   button on the edit form of an Article node doesn't open its
-        //   confirmation form in a dialog.
-        if (this.$form.find(':file').length) {
-          return;
-        }
       }
     }
 
diff --git a/core/modules/file/file.routing.yml b/core/modules/file/file.routing.yml
index d9d4efa..b7d2e6a 100644
--- a/core/modules/file/file.routing.yml
+++ b/core/modules/file/file.routing.yml
@@ -1,12 +1,3 @@
-file.ajax_upload:
-  path: '/file/ajax'
-  defaults:
-    _controller: '\Drupal\file\Controller\FileWidgetAjaxController::upload'
-  options:
-    _theme: ajax_base_page
-  requirements:
-    _permission: 'access content'
-
 file.ajax_progress:
   path: '/file/progress'
   defaults:
diff --git a/core/modules/file/src/Controller/FileWidgetAjaxController.php b/core/modules/file/src/Controller/FileWidgetAjaxController.php
index 90176f8..ff0c537 100644
--- a/core/modules/file/src/Controller/FileWidgetAjaxController.php
+++ b/core/modules/file/src/Controller/FileWidgetAjaxController.php
@@ -7,13 +7,8 @@
 
 namespace Drupal\file\Controller;
 
-use Drupal\Component\Utility\NestedArray;
-use Drupal\Core\Ajax\AjaxResponse;
-use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\system\Controller\FormAjaxController;
 use Symfony\Component\HttpFoundation\JsonResponse;
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 
 /**
  * Defines a controller to respond to file widget AJAX requests.
@@ -21,74 +16,6 @@
 class FileWidgetAjaxController extends FormAjaxController {
 
   /**
-   * Processes AJAX file uploads and deletions.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The current request object.
-   *
-   * @return \Drupal\Core\Ajax\AjaxResponse
-   *   An AjaxResponse object.
-   */
-  public function upload(Request $request) {
-    $form_parents = explode('/', $request->query->get('element_parents'));
-    $form_build_id = $request->query->get('form_build_id');
-    $request_form_build_id = $request->request->get('form_build_id');
-
-    if (empty($request_form_build_id) || $form_build_id !== $request_form_build_id) {
-      // Invalid request.
-      drupal_set_message(t('An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (@size) that this server supports.', array('@size' => format_size(file_upload_max_size()))), 'error');
-      $response = new AjaxResponse();
-      $status_messages = array('#type' => 'status_messages');
-      return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
-    }
-
-    try {
-      /** @var $ajaxForm \Drupal\system\FileAjaxForm */
-      $ajaxForm = $this->getForm($request);
-      $form = $ajaxForm->getForm();
-      $form_state = $ajaxForm->getFormState();
-      $commands = $ajaxForm->getCommands();
-    }
-    catch (HttpExceptionInterface $e) {
-      // Invalid form_build_id.
-      drupal_set_message(t('An unrecoverable error occurred. Use of this form has expired. Try reloading the page and submitting again.'), 'error');
-      $response = new AjaxResponse();
-      $status_messages = array('#type' => 'status_messages');
-      return $response->addCommand(new ReplaceCommand(NULL, $this->renderer->renderRoot($status_messages)));
-    }
-
-    // Get the current element and count the number of files.
-    $current_element = NestedArray::getValue($form, $form_parents);
-    $current_file_count = isset($current_element['#file_upload_delta']) ? $current_element['#file_upload_delta'] : 0;
-
-    // Process user input. $form and $form_state are modified in the process.
-    $this->formBuilder->processForm($form['#form_id'], $form, $form_state);
-
-    // Retrieve the element to be rendered.
-    $form = NestedArray::getValue($form, $form_parents);
-
-    // Add the special Ajax class if a new file was added.
-    if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']) {
-      $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content';
-    }
-    // Otherwise just add the new content class on a placeholder.
-    else {
-      $form['#suffix'] .= '<span class="ajax-new-content"></span>';
-    }
-
-    $status_messages = array('#type' => 'status_messages');
-    $form['#prefix'] .= $this->renderer->renderRoot($status_messages);
-    $output = $this->renderer->renderRoot($form);
-
-    $response = new AjaxResponse();
-    $response->setAttachments($form['#attached']);
-    foreach ($commands as $command) {
-      $response->addCommand($command, TRUE);
-    }
-    return $response->addCommand(new ReplaceCommand(NULL, $output));
-  }
-
-  /**
    * Returns the progress status for a file upload process.
    *
    * @param string $key
diff --git a/core/modules/file/src/Element/ManagedFile.php b/core/modules/file/src/Element/ManagedFile.php
index 066e33e..cb1f626 100644
--- a/core/modules/file/src/Element/ManagedFile.php
+++ b/core/modules/file/src/Element/ManagedFile.php
@@ -7,11 +7,15 @@
 
 namespace Drupal\file\Element;
 
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Component\Utility\Html;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Render\Element\FormElement;
 use Drupal\Core\Url;
 use Drupal\file\Entity\File;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Provides an AJAX/progress aware widget for uploading and saving a file.
@@ -125,6 +129,53 @@ public static function valueCallback(&$element, $input, FormStateInterface $form
   }
 
   /**
+   * #ajax callback for managed_file upload forms.
+   *
+   * This ajax callback takes care of the following things:
+   *   - Ensures that broken requests due to too big files are caught.
+   *   - Adds a class to the response to be able to highlight in the UI, that a
+   *     new file got uploaded.
+   *
+   * @param array $form
+   *   The build form.
+   * @param \Drupal\Core\Form\FormStateInterface $form_state
+   *   The form state.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request.
+   *
+   * @return \Drupal\Core\Ajax\AjaxResponse
+   *   The ajax response of the ajax upload.
+   */
+  public static function uploadAjaxCallback(&$form, FormStateInterface &$form_state, Request $request) {
+    /** @var \Drupal\Core\Render\RendererInterface $renderer */
+    $renderer = \Drupal::service('renderer');
+
+    $form_parents = explode('/', $request->query->get('element_parents'));
+
+    // Retrieve the element to be rendered.
+    $form = NestedArray::getValue($form, $form_parents);
+
+    // Add the special AJAX class if a new file was added.
+    $current_file_count = $form_state->get('file_upload_delta_initial');
+    if (isset($form['#file_upload_delta']) && $current_file_count < $form['#file_upload_delta']) {
+      $form[$current_file_count]['#attributes']['class'][] = 'ajax-new-content';
+    }
+    // Otherwise just add the new content class on a placeholder.
+    else {
+      $form['#suffix'] .= '<span class="ajax-new-content"></span>';
+    }
+
+    $status_messages = ['#type' => 'status_messages'];
+    $form['#prefix'] .= $renderer->renderRoot($status_messages);
+    $output = $renderer->renderRoot($form);
+
+    $response = new AjaxResponse();
+    $response->setAttachments($form['#attached']);
+
+    return $response->addCommand(new ReplaceCommand(NULL, $output));
+  }
+
+  /**
    * Render API callback: Expands the managed_file element type.
    *
    * Expands the file type to include Upload and Remove buttons, as well as
@@ -146,12 +197,10 @@ public static function processManagedFile(&$element, FormStateInterface $form_st
     $ajax_wrapper_id = Html::getUniqueId('ajax-wrapper');
 
     $ajax_settings = [
-      // @todo Remove this in https://www.drupal.org/node/2500527.
-      'url' => Url::fromRoute('file.ajax_upload'),
+      'callback' => [get_called_class(), 'uploadAjaxCallback'],
       'options' => [
         'query' => [
           'element_parents' => implode('/', $element['#array_parents']),
-          'form_build_id' => $complete_form['form_build_id']['#value'],
         ],
       ],
       'wrapper' => $ajax_wrapper_id,
diff --git a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
index da48f74..f184870 100644
--- a/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
+++ b/core/modules/file/src/Plugin/Field/FieldWidget/FileWidget.php
@@ -407,18 +407,15 @@ public static function process($element, FormStateInterface $form_state, $form)
     // file, the entire group of file fields is updated together.
     if ($element['#cardinality'] != 1) {
       $parents = array_slice($element['#array_parents'], 0, -1);
-      $new_url = Url::fromRoute('file.ajax_upload');
       $new_options = array(
         'query' => array(
           'element_parents' => implode('/', $parents),
-          'form_build_id' => $form['form_build_id']['#value'],
         ),
       );
       $field_element = NestedArray::getValue($form, $parents);
       $new_wrapper = $field_element['#id'] . '-ajax-wrapper';
       foreach (Element::children($element) as $key) {
         if (isset($element[$key]['#ajax'])) {
-          $element[$key]['#ajax']['url'] = $new_url->setOptions($new_options);
           $element[$key]['#ajax']['options'] = $new_options;
           $element[$key]['#ajax']['wrapper'] = $new_wrapper;
         }
@@ -451,6 +448,19 @@ public static function processMultiple($element, FormStateInterface $form_state,
     $element_children = Element::children($element, TRUE);
     $count = count($element_children);
 
+    // Count the number of already uploaded files, in order to display new
+    // items in \Drupal\file\Element\ManagedFile::uploadAjaxCallback().
+    if (!$form_state->isRebuilding()) {
+      $count_items_before = 0;
+      foreach ($element_children as $children) {
+        if (!empty($element[$children]['#default_value']['fids'])) {
+          $count_items_before++;
+        }
+      }
+
+      $form_state->set('file_upload_delta_initial', $count_items_before);
+    }
+
     foreach ($element_children as $delta => $key) {
       if ($key != $element['#file_upload_delta']) {
         $description = static::getDescriptionFromElement($element[$key]);
diff --git a/core/modules/system/src/Tests/Ajax/AjaxFormCacheTest.php b/core/modules/system/src/Tests/Ajax/AjaxFormCacheTest.php
index 08ade26..f151c33 100644
--- a/core/modules/system/src/Tests/Ajax/AjaxFormCacheTest.php
+++ b/core/modules/system/src/Tests/Ajax/AjaxFormCacheTest.php
@@ -64,7 +64,7 @@ public function testBlockForms() {
     $this->drupalPostAjaxForm(NULL, ['test1' => 'option1'], 'test1');
     $this->assertOptionSelectedWithDrupalSelector('edit-test1', 'option1');
     $this->assertOptionWithDrupalSelector('edit-test1', 'option3');
-    $this->drupalPostForm($this->getUrl(), ['test1' => 'option1'], 'Submit');
+    $this->drupalPostForm(NULL, ['test1' => 'option1'], 'Submit');
     $this->assertText('Submission successful.');
   }
 
diff --git a/core/modules/user/src/Tests/UserRegistrationTest.php b/core/modules/user/src/Tests/UserRegistrationTest.php
index cc5785a..0b81ec0 100644
--- a/core/modules/user/src/Tests/UserRegistrationTest.php
+++ b/core/modules/user/src/Tests/UserRegistrationTest.php
@@ -7,7 +7,10 @@
 
 namespace Drupal\user\Tests;
 
+use Drupal\Core\Entity\Entity\EntityFormDisplay;
 use Drupal\Core\Field\FieldStorageDefinitionInterface;
+use Drupal\field\Entity\FieldConfig;
+use Drupal\field\Entity\FieldStorageConfig;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -150,6 +153,72 @@ function testRegistrationEmailDuplicates() {
     $this->assertText(t('The email address @email is already taken.', array('@email' => $duplicate_user->getEmail())), 'Supplying a duplicate email address with added whitespace displays an error message');
   }
 
+  /**
+   * Tests that UUID isn't cached in form state on register form.
+   */
+  public function testUuidFormState() {
+    \Drupal::service('module_installer')->install(['image']);
+    \Drupal::service('router.builder')->rebuild();
+
+    // Add a picture field in order to ensure that no form cache is added, which
+    // breaks registration of more than 1 user every 6 hour.
+    $field_storage = FieldStorageConfig::create([
+      'field_name' => 'user_picture',
+      'entity_type' => 'user',
+      'type' => 'image',
+    ]);
+    $field_storage->save();
+
+    $field = FieldConfig::create([
+      'field_name' => 'user_picture',
+      'entity_type' => 'user',
+      'bundle' => 'user',
+    ]);
+    $field->save();
+
+    $form_display = EntityFormDisplay::create([
+      'targetEntityType' => 'user',
+      'bundle' => 'user',
+      'mode' => 'default',
+      'status' => TRUE,
+    ]);
+    $form_display->setComponent('user_picture', [
+      'type' => 'image_image',
+    ]);
+    $form_display->save();
+
+    // Don't require email verification and allow registration by site visitors
+    // without administrator approval.
+    $this->config('user.settings')
+      ->set('verify_mail', FALSE)
+      ->set('register', USER_REGISTER_VISITORS)
+      ->save();
+
+    $edit = [];
+    $edit['name'] = $this->randomMachineName();
+    $edit['mail'] = $edit['name'] . '@example.com';
+    $edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
+
+    // Create one account.
+    $this->drupalPostForm('user/register', $edit, t('Create new account'));
+    $this->assertResponse(200);
+
+    $user_storage = \Drupal::entityManager()->getStorage('user');
+
+    $this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
+    $this->drupalLogout();
+
+    // Create a second account.
+    $edit['name'] = $this->randomMachineName();
+    $edit['mail'] = $edit['name'] . '@example.com';
+    $edit['pass[pass2]'] = $edit['pass[pass1]'] = $this->randomMachineName();
+
+    $this->drupalPostForm('user/register', $edit, t('Create new account'));
+    $this->assertResponse(200);
+
+    $this->assertTrue($user_storage->loadByProperties(['name' => $edit['name']]));
+  }
+
   function testRegistrationDefaultValues() {
     // Don't require email verification and allow registration by site visitors
     // without administrator approval.
diff --git a/core/modules/user/user.routing.yml b/core/modules/user/user.routing.yml
index 6dc4fdb..6eb709f 100644
--- a/core/modules/user/user.routing.yml
+++ b/core/modules/user/user.routing.yml
@@ -5,9 +5,6 @@ user.register:
     _title: 'Create new account'
   requirements:
     _access_user_register: 'TRUE'
-  # @todo Remove when https://www.drupal.org/node/2465053 lands.
-  options:
-    no_cache: TRUE
 
 user.logout:
   path: '/user/logout'
diff --git a/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php b/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
index eb01501..de67c43 100644
--- a/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/EventSubscriber/FormAjaxSubscriberTest.php
@@ -7,13 +7,16 @@
 
 namespace Drupal\Tests\Core\Form\EventSubscriber;
 
+use Drupal\Core\Ajax\AjaxResponse;
 use Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber;
 use Drupal\Core\Form\FormAjaxException;
+use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Tests\UnitTestCase;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
 use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 
@@ -39,6 +42,13 @@ class FormAjaxSubscriberTest extends UnitTestCase {
   protected $httpKernel;
 
   /**
+   * The renderer service.
+   *
+   * @var \Drupal\Core\Render\RendererInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $renderer;
+
+  /**
    * {@inheritdoc}
    */
   protected function setUp() {
@@ -46,7 +56,8 @@ protected function setUp() {
 
     $this->httpKernel = $this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface');
     $this->formAjaxResponseBuilder = $this->getMock('Drupal\Core\Form\FormAjaxResponseBuilderInterface');
-    $this->subscriber = new FormAjaxSubscriber($this->formAjaxResponseBuilder);
+    $this->renderer = $this->getMock('\Drupal\Core\Render\RendererInterface');
+    $this->subscriber = new FormAjaxSubscriber($this->formAjaxResponseBuilder, $this->renderer);
   }
 
   /**
@@ -135,6 +146,42 @@ public function testOnExceptionResponseBuilderException() {
 
   /**
    * @covers ::onException
+   */
+  public function testOnExceptionBadRequest() {
+    $this->formAjaxResponseBuilder->expects($this->never())
+      ->method('buildResponse');
+    $this->subscriber = $this->getMockBuilder('\Drupal\Core\Form\EventSubscriber\FormAjaxSubscriber')
+      ->setConstructorArgs([$this->formAjaxResponseBuilder, $this->renderer])
+      ->setMethods(['drupalSetMessage'])
+      ->getMock();
+    $this->subscriber->expects($this->once())
+      ->method('drupalSetMessage')
+      ->willReturn('asdf');
+    $rendered_output = 'the rendered output';
+    $this->renderer->expects($this->once())
+      ->method('renderRoot')
+      ->willReturn($rendered_output);
+
+    $exception = new BadRequestHttpException();
+    $request = new Request([FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]);
+
+    $event = new GetResponseForExceptionEvent($this->httpKernel, $request, HttpKernelInterface::MASTER_REQUEST, $exception);
+    $this->subscriber->onException($event);
+    $actual_response = $event->getResponse();
+    $this->assertInstanceOf('\Drupal\Core\Ajax\AjaxResponse', $actual_response);
+    $this->assertSame(200, $actual_response->headers->get('X-Status-Code'));
+    $expected_commands[] = [
+      'command' => 'insert',
+      'method' => 'replaceWith',
+      'selector' => NULL,
+      'data' => $rendered_output,
+      'settings' => NULL,
+    ];
+    $this->assertSame($expected_commands, $actual_response->getCommands());
+  }
+
+  /**
+   * @covers ::onException
    * @covers ::getFormAjaxException
    */
   public function testOnExceptionNestedException() {
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index 77c8763..69c50da 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -9,10 +9,13 @@
 
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Form\EnforcedResponseException;
+use Drupal\Core\Form\FormBuilderInterface;
 use Drupal\Core\Form\FormInterface;
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
  * @coversDefaultClass \Drupal\Core\Form\FormBuilder
@@ -423,6 +426,30 @@ public function testFormCacheDeletionUncached() {
     $this->simulateFormSubmission($form_id, $form_arg, $form_state);
   }
 
+  /**
+   * @covers ::buildForm
+   *
+   * @expectedException \Symfony\Component\HttpKernel\Exception\BadRequestHttpException
+   * @expectedExceptionMessage The uploaded file likely exceeded the maximum file size (32M) that this server supports.
+   */
+  public function testExceededFileSize() {
+    $request = new Request([FormBuilderInterface::AJAX_FORM_REQUEST => TRUE]);
+    $request_stack = new RequestStack();
+    $request_stack->push($request);
+    $this->formBuilder = $this->getMockBuilder('\Drupal\Core\Form\FormBuilder')
+      ->setConstructorArgs([$this->formValidator, $this->formSubmitter, $this->formCache, $this->moduleHandler, $this->eventDispatcher, $request_stack, $this->classResolver, $this->elementInfo, $this->themeManager, $this->getStringTranslationStub(), $this->csrfToken])
+      ->setMethods(['getFormattedFileUploadMaxSize'])
+      ->getMock();
+    $this->formBuilder->expects($this->once())
+      ->method('getFormattedFileUploadMaxSize')
+      ->willReturn('32M');
+
+    $form_arg = $this->getMockForm('test_form_id');
+    $form_state = new FormState();
+
+    $this->formBuilder->buildForm($form_arg, $form_state);
+  }
+
 }
 
 class TestForm implements FormInterface {
diff --git a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
index b6fe9dd..fb48edf 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormTestBase.php
@@ -149,7 +149,19 @@
    */
   protected $themeManager;
 
+  /**
+   * The string translation service.
+   *
+   * @var \Drupal\Core\StringTranslation\TranslationInterface|\PHPUnit_Framework_MockObject_MockObject
+   */
+  protected $stringTranslation;
+
+  /**
+   * {@inheritdoc}
+   */
   protected function setUp() {
+    parent::setUp();
+
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
 
     $this->formCache = $this->getMock('Drupal\Core\Form\FormCacheInterface');
@@ -188,8 +200,9 @@ protected function setUp() {
       ->setMethods(array('batchGet', 'drupalInstallationAttempted'))
       ->getMock();
     $this->root = dirname(dirname(substr(__DIR__, 0, -strlen(__NAMESPACE__))));
+    $this->stringTranslation = $this->getStringTranslationStub();
 
-    $this->formBuilder = new FormBuilder($this->formValidator, $this->formSubmitter, $this->formCache, $this->moduleHandler, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->elementInfo, $this->themeManager, $this->csrfToken, $this->kernel);
+    $this->formBuilder = new FormBuilder($this->formValidator, $this->formSubmitter, $this->formCache, $this->moduleHandler, $this->eventDispatcher, $this->requestStack, $this->classResolver, $this->elementInfo, $this->themeManager, $this->stringTranslation, $this->csrfToken);
   }
 
   /**
