diff --git a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
index 7d36b51c718e879487ee56a5dde58ceffb43fb37..69a8f13788875b6c428e6fcb4c3adb2b8e29d0b1 100644
--- a/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Ajax/AjaxResponseAttachmentsProcessor.php
@@ -148,6 +148,16 @@ protected function buildAttachmentsCommands(AjaxResponse $response, Request $req
     $css_assets = $this->assetResolver->getCssAssets($assets, $optimize_css);
     list($js_assets_header, $js_assets_footer) = $this->assetResolver->getJsAssets($assets, $optimize_js);
 
+    // First, AttachedAssets::setLibraries() ensures duplicate libraries are
+    // removed: it converts it to a set of libraries if necessary. Second,
+    // AssetResolver::getJsSettings() ensures $assets contains the final set of
+    // JavaScript settings. AttachmentsResponseProcessorInterface also mandates
+    // that the response it processes contains the final attachment values, so
+    // update both the 'library' and 'drupalSettings' attachments accordingly.
+    $attachments['library'] = $assets->getLibraries();
+    $attachments['drupalSettings'] = $assets->getSettings();
+    $response->setAttachments($attachments);
+
     // Render the HTML to load these files, and add AJAX commands to insert this
     // HTML in the page. Settings are handled separately, afterwards.
     $settings = [];
diff --git a/core/lib/Drupal/Core/Asset/AssetResolver.php b/core/lib/Drupal/Core/Asset/AssetResolver.php
index 9927049bea6ef5a46c3a6746d06e8caeab633f0d..a533c8ad61df0c94129bccf971e1b9e1dddaeeec 100644
--- a/core/lib/Drupal/Core/Asset/AssetResolver.php
+++ b/core/lib/Drupal/Core/Asset/AssetResolver.php
@@ -334,6 +334,9 @@ public function getJsAssets(AttachedAssetsInterface $assets, $optimize) {
       // Allow modules and themes to alter the JavaScript settings.
       $this->moduleHandler->alter('js_settings', $settings, $assets);
       $this->themeManager->alter('js_settings', $settings, $assets);
+      // Update the $assets object accordingly, so that it reflects the final
+      // settings.
+      $assets->setSettings($settings);
       $settings_as_inline_javascript = [
         'type' => 'setting',
         'group' => JS_SETTING,
diff --git a/core/lib/Drupal/Core/Asset/AssetResolverInterface.php b/core/lib/Drupal/Core/Asset/AssetResolverInterface.php
index c912d7663c4968638546362f9744906405bef72f..e0845c62ee877146535cd29884196c94dedb66b2 100644
--- a/core/lib/Drupal/Core/Asset/AssetResolverInterface.php
+++ b/core/lib/Drupal/Core/Asset/AssetResolverInterface.php
@@ -69,6 +69,8 @@ public function getCssAssets(AttachedAssetsInterface $assets, $optimize);
    *
    * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
    *   The assets attached to the current response.
+   *   Note that this object is modified to reflect the final JavaScript
+   *   settings assets.
    * @param bool $optimize
    *   Whether to apply the JavaScript asset collection optimizer, to return
    *   optimized JavaScript asset collections rather than an unoptimized ones.
diff --git a/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php b/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php
index 6067a862571183f356d1821577a55e7bba6d3f46..74614eec077a9ba78c59361a2109160b093bb549 100644
--- a/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php
+++ b/core/lib/Drupal/Core/Render/AttachmentsResponseProcessorInterface.php
@@ -46,7 +46,8 @@ interface AttachmentsResponseProcessorInterface {
    *   The response to process.
    *
    * @return \Drupal\Core\Render\AttachmentsInterface
-   *   The processed response.
+   *   The processed response, with the attachments updated to reflect their
+   *   final values.
    *
    * @throws \InvalidArgumentException
    *   Thrown when the $response parameter is not the type of response object
diff --git a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
index e0160d1b46a9063cb18e16832ea230ccf2edd7e6..d4070629c62686666cd83ad917f8c427ab57b6f7 100644
--- a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
@@ -9,6 +9,7 @@
 use Drupal\Core\Asset\AssetCollectionRendererInterface;
 use Drupal\Core\Asset\AssetResolverInterface;
 use Drupal\Core\Asset\AttachedAssets;
+use Drupal\Core\Asset\AttachedAssetsInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Form\EnforcedResponseException;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -155,7 +156,19 @@ public function processAttachments(AttachmentsInterface $response) {
       $attachment_placeholders = $attached['html_response_attachment_placeholders'];
       unset($attached['html_response_attachment_placeholders']);
 
-      $variables = $this->processAssetLibraries($attached, $attachment_placeholders);
+      $assets = AttachedAssets::createFromRenderArray(['#attached' => $attached]);
+      // Take Ajax page state into account, to allow for something like
+      // Turbolinks to be implemented without altering core.
+      // @see https://github.com/rails/turbolinks/
+      $ajax_page_state = $this->requestStack->getCurrentRequest()->get('ajax_page_state');
+      $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []);
+      $variables = $this->processAssetLibraries($assets, $attachment_placeholders);
+      // $variables now contains the markup to load the asset libraries. Update
+      // $attached with the final list of libraries and JavaScript settings, so
+      // that $response can be updated with those. Then the response object will
+      // list the final, processed attachments.
+      $attached['library'] = $assets->getLibraries();
+      $attached['drupalSettings'] = $assets->getSettings();
 
       // Since we can only replace content in the HTML head section if there's a
       // placeholder for it, we can safely avoid processing the render array if
@@ -168,6 +181,7 @@ public function processAttachments(AttachmentsInterface $response) {
             $attached,
             $this->processFeed($attached['feed'])
           );
+          unset($attached['feed']);
         }
         // 'html_head_link' is a special case of 'html_head' which can be present
         // as a head element, but also as a Link: HTTP header depending on
@@ -182,6 +196,7 @@ public function processAttachments(AttachmentsInterface $response) {
             $attached,
             $this->processHtmlHeadLink($attached['html_head_link'])
           );
+          unset($attached['html_head_link']);
         }
 
         // Now we can process 'html_head', which contains both 'feed' and
@@ -200,6 +215,10 @@ public function processAttachments(AttachmentsInterface $response) {
       $this->setHeaders($response, $attached['http_header']);
     }
 
+    // AttachmentsResponseProcessorInterface mandates that the response it
+    // processes contains the final attachment values.
+    $response->setAttachments($attached);
+
     return $response;
   }
 
@@ -255,8 +274,8 @@ protected function renderPlaceholders(HtmlResponse $response) {
   /**
    * Processes asset libraries into render arrays.
    *
-   * @param array $attached
-   *   The attachments to process.
+   * @param \Drupal\Core\Asset\AttachedAssetsInterface $assets
+   *   The attached assets collection for the current response.
    * @param array $placeholders
    *   The placeholders that exist in the response.
    *
@@ -266,16 +285,7 @@ protected function renderPlaceholders(HtmlResponse $response) {
    *     - scripts
    *     - scripts_bottom
    */
-  protected function processAssetLibraries(array $attached, array $placeholders) {
-    $all_attached = ['#attached' => $attached];
-    $assets = AttachedAssets::createFromRenderArray($all_attached);
-
-    // Take Ajax page state into account, to allow for something like Turbolinks
-    // to be implemented without altering core.
-    // @see https://github.com/rails/turbolinks/
-    $ajax_page_state = $this->requestStack->getCurrentRequest()->get('ajax_page_state');
-    $assets->setAlreadyLoadedLibraries(isset($ajax_page_state) ? explode(',', $ajax_page_state['libraries']) : []);
-
+  protected function processAssetLibraries(AttachedAssetsInterface $assets, array $placeholders) {
     $variables = [];
 
     // Print styles - if present.
diff --git a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php
index 7440d995df6aa274dff590f1a29209ee9d56db54..cc04606bcc9f65ea46e34576c88b637cecc52a1d 100644
--- a/core/modules/system/src/Tests/Common/AttachedAssetsTest.php
+++ b/core/modules/system/src/Tests/Common/AttachedAssetsTest.php
@@ -108,8 +108,10 @@ function testAddJsSettings() {
     $build['#attached']['library'][] = 'core/drupalSettings';
     $assets = AttachedAssets::createFromRenderArray($build);
 
+    $this->assertEqual([], $assets->getSettings(), 'JavaScript settings on $assets are empty.');
     $javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];
     $this->assertTrue(array_key_exists('currentPath', $javascript['drupalSettings']['data']['path']), 'The current path JavaScript setting is set correctly.');
+    $this->assertTrue(array_key_exists('currentPath', $assets->getSettings()['path']), 'JavaScript settings on $assets are resolved after retrieving JavaScript assets, and are equal to the returned JavaScript settings.');
 
     $assets->setSettings(['drupal' => 'rocks', 'dries' => 280342800]);
     $javascript = $this->assetResolver->getJsAssets($assets, FALSE)[1];