diff --git a/core/core.services.yml b/core/core.services.yml
index 9d40627..90e64bd 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -1057,7 +1057,7 @@ services:
   html_response.attachments_processor:
     class: Drupal\Core\Render\HtmlResponseAttachmentsProcessor
     tags:
-    arguments: ['@asset.resolver', '@config.factory', '@asset.css.collection_renderer', '@asset.js.collection_renderer', '@request_stack', '@renderer']
+    arguments: ['@asset.resolver', '@config.factory', '@asset.css.collection_renderer', '@asset.js.collection_renderer', '@request_stack', '@renderer', '@module_handler']
   html_response.subscriber:
     class: Drupal\Core\EventSubscriber\HtmlResponseSubscriber
     tags:
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 006c58f..175267f 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -148,62 +148,6 @@
 const LOCALE_PLURAL_DELIMITER = "\03";
 
 /**
- * Adds output to the HEAD tag of the HTML page.
- *
- * This function can be called as long as the headers aren't sent. Pass no
- * arguments (or NULL for both) to retrieve the currently stored elements.
- *
- * @param $data
- *   A renderable array. If the '#type' key is not set then 'html_tag' will be
- *   added as the default '#type'.
- * @param $key
- *   A unique string key to allow implementations of hook_html_head_alter() to
- *   identify the element in $data. Required if $data is not NULL.
- *
- * @return
- *   An array of all stored HEAD elements.
- *
- * @see \Drupal\Core\Render\Element\HtmlTag::preRenderHtmlTag()
- *
- * @deprecated in Drupal 8.0.x, will be removed before Drupal 8.0.0
- *   Use #attached on render arrays.
- */
-function _drupal_add_html_head($data = NULL, $key = NULL) {
-  $stored_head = &drupal_static(__FUNCTION__, array());
-
-  if (isset($data) && isset($key)) {
-    if (!isset($data['#type'])) {
-      $data['#type'] = 'html_tag';
-    }
-    $stored_head[$key] = $data;
-  }
-  return $stored_head;
-}
-
-/**
- * Retrieves output to be displayed in the HEAD tag of the HTML page.
- *
- * @param bool $render
- *   If TRUE render the HEAD elements, otherwise return just the elements.
- *
- * @return string|array
- *   Return the rendered HTML head or the elements itself.
- *
- * @deprecated in Drupal 8.0.x, will be removed before Drupal 8.0.0
- *   Use #attached on render arrays.
- */
-function drupal_get_html_head($render = TRUE) {
-  $elements = _drupal_add_html_head();
-  \Drupal::moduleHandler()->alter('html_head', $elements);
-  if ($render) {
-    return \Drupal::service('renderer')->renderPlain($elements);
-  }
-  else {
-    return $elements;
-  }
-}
-
-/**
  * Prepares a 'destination' URL query parameter for use with url().
  *
  * Used to direct the user back to the referring page after completing a form.
@@ -464,39 +408,6 @@ function base_path() {
 }
 
 /**
- * Adds a LINK tag with a distinct 'rel' attribute to the page's HEAD.
- *
- * This function can be called as long the HTML header hasn't been sent, which
- * on normal pages is up through the preprocess step of _theme('html'). Adding
- * a link will overwrite a prior link with the exact same 'rel' and 'href'
- * attributes.
- *
- * @param $attributes
- *   Associative array of element attributes including 'href' and 'rel'.
- * @param $header
- *   Optional flag to determine if a HTTP 'Link:' header should be sent.
- *
- * @deprecated in Drupal 8.0.x, will be removed before Drupal 8.0.0
- *   Use #attached on render arrays.
- */
-function _drupal_add_html_head_link($attributes, $header = FALSE) {
-  $element = array(
-    '#tag' => 'link',
-    '#attributes' => $attributes,
-  );
-  $href = $attributes['href'];
-
-  if ($header) {
-    // Also add a HTTP header "Link:".
-    $href = '<' . SafeMarkup::checkPlain($attributes['href']) . '>;';
-    unset($attributes['href']);
-    $element['#attached']['http_header'][] = array('Link',  $href . drupal_http_header_attributes($attributes), TRUE);
-  }
-
-  _drupal_add_html_head($element, 'html_head_link:' . $attributes['rel'] . ':' . $href);
-}
-
-/**
  * Deletes old cached CSS files.
  *
  * @deprecated in Drupal 8.x, will be removed before Drupal 9.0.
@@ -615,6 +526,9 @@ function drupal_merge_attached(array $a, array $b) {
  *
  * @throws LogicException
  *   When attaching something of a non-existing attachment type.
+ *
+ * @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0. Specify
+ *   attached resources and directives in the render array.
  */
 function drupal_process_attached(array $elements) {
   // Asset attachments are handled by \Drupal\Core\Asset\AssetResolver.
@@ -628,22 +542,16 @@ function drupal_process_attached(array $elements) {
       // Limit the amount allowed entries.
       switch ($callback) {
         case 'html_head':
-          call_user_func_array('_drupal_add_html_head', $args);
+          // @todo Removed in https://www.drupal.org/node/2477223
           break;
         case 'feed':
-          $args = [[
-            'href' => $args[0],
-            'rel' => 'alternate',
-            'title' => $args[1],
-            'type' => 'application/rss+xml',
-          ]];
-          call_user_func_array('_drupal_add_html_head_link', $args);
+          // @todo Removed in https://www.drupal.org/node/2477223
           break;
         case 'html_head_link':
-          call_user_func_array('_drupal_add_html_head_link', $args);
+          // @todo Removed in https://www.drupal.org/node/2477223
           break;
         case 'http_header':
-          // @todo Remove validation in https://www.drupal.org/node/2477223
+          // @todo Removed in https://www.drupal.org/node/2467759
           break;
         default:
           throw new \LogicException(sprintf('You are not allowed to use %s in #attached', $callback));
diff --git a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
index 0cd7417..b5b3c27 100644
--- a/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
+++ b/core/lib/Drupal/Core/Render/HtmlResponseAttachmentsProcessor.php
@@ -10,6 +10,8 @@
 use Drupal\Core\Asset\AssetResolverInterface;
 use Drupal\Core\Asset\AttachedAssets;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Component\Utility\SafeMarkup;
 use Symfony\Component\HttpFoundation\RequestStack;
 
 /**
@@ -66,6 +68,13 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
   protected $renderer;
 
   /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
    * Constructs a HtmlResponseAttachmentsProcessor object.
    *
    * @param \Drupal\Core\Asset\AssetResolverInterface $asset_resolver
@@ -80,14 +89,17 @@ class HtmlResponseAttachmentsProcessor implements AttachmentsResponseProcessorIn
    *   The request stack.
    * @param \Drupal\Core\Render\RendererInterface $renderer
    *   The renderer.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler service.
    */
-  public function __construct(AssetResolverInterface $asset_resolver, ConfigFactoryInterface $config_factory, AssetCollectionRendererInterface $css_collection_renderer, AssetCollectionRendererInterface $js_collection_renderer, RequestStack $request_stack, RendererInterface $renderer) {
+  public function __construct(AssetResolverInterface $asset_resolver, ConfigFactoryInterface $config_factory, AssetCollectionRendererInterface $css_collection_renderer, AssetCollectionRendererInterface $js_collection_renderer, RequestStack $request_stack, RendererInterface $renderer, ModuleHandlerInterface $module_handler) {
     $this->assetResolver = $asset_resolver;
     $this->config = $config_factory->get('system.performance');
     $this->cssCollectionRenderer = $css_collection_renderer;
     $this->jsCollectionRenderer = $js_collection_renderer;
     $this->requestStack = $request_stack;
     $this->renderer = $renderer;
+    $this->moduleHandler = $module_handler;
   }
 
   /**
@@ -107,19 +119,70 @@ public function processAttachments(AttachmentsInterface $response) {
 
     $variables = $this->processAssetLibraries($attached, $placeholders);
 
-    // Handle all non-asset attachments. This populates drupal_get_html_head().
-    $all_attached = ['#attached' => $attached];
-    drupal_process_attached($all_attached);
+    // 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
+    // it's not present.
+    if (!empty($placeholders['head'])) {
 
-    // Get HTML head elements - if present.
-    if (isset($placeholders['head'])) {
-      $variables['head'] = drupal_get_html_head(FALSE);
+      // feed is a special casse of html_head_link. We create an html_head_link
+      // for the RSS feed.
+      if (!empty($attached['feed'])) {
+        // @todo: Determine if feed is supposed to be a single structure or an
+        // array of structures.
+        $feed = $attached['feed'];
+
+        if (is_array($feed[0])) {
+          foreach ($feed as $item) {
+            $feed_link = [
+              'href' => $item[0],
+              'rel' => 'alternate',
+              'title' => empty($feed[1]) ? '' : $item[1],
+              'type' => 'application/rss+xml',
+            ];
+            $attached['html_head_link'][] = [$feed_link];
+          }
+        }
+        else {
+          $feed_link = [
+            'href' => $feed[0],
+            'rel' => 'alternate',
+            'title' => empty($feed[1]) ? '' : $feed[1],
+            'type' => 'application/rss+xml',
+          ];
+          $attached['html_head_link'][] = $feed_link;
+        }
+      }
+
+      // 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 settings
+      // in the render array. Processing it can add to both the html_head and
+      // http_header keys of #attached, so we must address it first.
+      if (!empty($attached['html_head_link'])) {
+        $link_attached = $this->processHtmlHeadLink($attached['html_head_link']);
+        // Merge $link_attached into $attached so that its html_head and
+        // http_header values are present for further processing. The merge
+        // requires a full render array, so we have to add #attached keys.
+        $attached = $this->renderer->mergeBubbleableMetadata(
+          ['#attached' => $attached],
+          ['#attached' => $link_attached]
+        );
+        // Unwrap the #attached key.
+        $attached = $attached['#attached'];
+      }
+
+      // Now we can process html_head.
+      if (!empty($attached['html_head'])) {
+        $head = $this->processHtmlHead($attached['html_head']);
+        // Invoke hook_html_head_alter().
+        $this->moduleHandler->alter('html_head', $head);
+        $variables['head'] = $head;
+      }
     }
 
-    // Now replace the placeholders in the response content with the real data.
+    // Replace the placeholders in the response content with the real data.
     $this->renderPlaceholders($response, $placeholders, $variables);
 
-    // Finally set the headers on the response if any bubbled.
+    // Set the HTTP headers on the response if any bubbled.
     if (!empty($attached['http_header'])) {
       $this->setHeaders($response, $attached['http_header']);
     }
@@ -219,4 +282,68 @@ protected function setHeaders(HtmlResponse $response, array $headers) {
     }
   }
 
+  /**
+   * Ensure proper key/data order and defaults for renderable head items.
+   *
+   * @param array $html_head
+   *   The ['#attached']['html_head'] portion of a render array.
+   *
+   * @return array
+   *   The ['#attached']['html_head'] portion of a render array with #type of
+   *   html_tag added for items without a #type.
+   */
+  protected function processHtmlHead(array $html_head) {
+    $head = [];
+    foreach ($html_head as $item) {
+      list ($data, $key) = $item;
+      if (!isset($data['#type'])) {
+        $data['#type'] = 'html_tag';
+      }
+      $head[$key] = $data;
+    }
+    return $head;
+  }
+
+  /**
+   * Transform a html_head_link array into html_head and http_header arrays.
+   *
+   * html_head_link is a special case of html_head which can be present as
+   * a link item in the HTML head section, and also as a Link: HTTP header,
+   * depending on options in the render array. Processing it can add to both the
+   * html_head and http_header sections.
+   *
+   * @param array $html_head_link
+   *   The 'html_head_link' value of a render array.
+   *
+   * @return array
+   *   An ['#attached'] section of a render array. This allows us to easily
+   *   merge the results with other render arrays. The array could contain the
+   *   following keys:
+   *   - http_header
+   *   - html_head
+   */
+  protected function processHtmlHeadLink(array $html_head_link) {
+    $attached = [];
+
+    foreach ($html_head_link as $item) {
+      $attributes = $item[0];
+      $should_add_header = isset($item[1]) ? $item[1] : FALSE;
+
+      $element = array(
+        '#tag' => 'link',
+        '#attributes' => $attributes,
+      );
+      $href = $attributes['href'];
+      $attached['html_head'][] = [$element, 'html_head_link:' . $attributes['rel'] . ':' . $href];
+
+      if ($should_add_header) {
+        // Also add a HTTP header "Link:".
+        $href = '<' . SafeMarkup::checkPlain($attributes['href']) . '>;';
+        unset($attributes['href']);
+        $attached['http_header'][] = ['Link', $href . drupal_http_header_attributes($attributes), TRUE];
+      }
+    }
+    return $attached;
+  }
+
 }
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index e80b5e3..355fd02 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -468,7 +468,9 @@ function template_preprocess_book_export_html(&$variables) {
   $variables['base_url'] = $base_url;
   $variables['language'] = $language_interface;
   $variables['language_rtl'] = ($language_interface->getDirection() == LanguageInterface::DIRECTION_RTL);
-  $variables['head'] = drupal_get_html_head();
+  // @todo: Commenting out the following line is wrong.
+  // @see https://www.drupal.org/node/2477223
+//  $variables['head'] = drupal_get_html_head();
 
   // HTML element attributes.
   $attributes = array();
diff --git a/core/modules/system/src/Tests/Common/AddFeedTest.php b/core/modules/system/src/Tests/Common/AddFeedTest.php
index a83f41b..7ec728e 100644
--- a/core/modules/system/src/Tests/Common/AddFeedTest.php
+++ b/core/modules/system/src/Tests/Common/AddFeedTest.php
@@ -61,9 +61,15 @@ function testBasicFeedAddNoTitle() {
       $build['#attached']['feed'][] = [$feed_info['url'], $feed_info['title']];
     }
 
-    drupal_process_attached($build);
+    // Use the bare HTML page renderer to render our links.
+    $renderer = $this->container->get('bare_html_page_renderer');
+    $response = $renderer->renderBarePage(
+      $build, '', $this->container->get('theme.manager')->getActiveTheme()->getName()
+    );
 
-    $this->setRawContent(drupal_get_html_head());
+    // Glean the content from the response object.
+    $this->setRawContent($response->getContent());
+    // Assert that the content contains the RSS links we specified.
     foreach ($urls as $description => $feed_info) {
       $this->assertPattern($this->urlToRSSLinkPattern($feed_info['url'], $feed_info['title']), format_string('Found correct feed header for %description', array('%description' => $description)));
     }
