diff --git a/core/modules/system/src/Tests/Render/HtmlResponseAttachmentsTest.php b/core/modules/system/src/Tests/Render/HtmlResponseAttachmentsTest.php
new file mode 100644
index 0000000..2656ccf
--- /dev/null
+++ b/core/modules/system/src/Tests/Render/HtmlResponseAttachmentsTest.php
@@ -0,0 +1,175 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\system\Tests\Render\HtmlResponseAttachmentsTest.
+ */
+
+namespace Drupal\system\Tests\Render;
+
+use Drupal\simpletest\WebTestBase;
+
+/**
+ * Functional tests for HtmlResponseAttachmentsProcessor.
+ *
+ * @group Render
+ */
+class HtmlResponseAttachmentsTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = ['render_attached_test'];
+
+  /**
+   * Test rendering of ['#attached'].
+   */
+  public function testAttachments() {
+    // Test ['#attached']['http_header] = ['Status', $code].
+    $this->drupalGet('/render_attached_test/teapot');
+    $this->assertResponse(418);
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/teapot');
+    $this->assertResponse(418);
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Test ['#attached']['http_header'] with various replacement rules.
+    $this->drupalGet('/render_attached_test/header');
+    $this->assertTeapotHeaders();
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/header');
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Test ['#attached']['feed'].
+    $this->drupalGet('/render_attached_test/feed');
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    $this->assertFeed();
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/feed');
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Test ['#attached']['html_head'].
+    $this->drupalGet('/render_attached_test/head');
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    $this->assertHead();
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/head');
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Now repeat all of the preceeding using drupal_process_attached().
+    // Test ['#attached']['http_header] = ['Status', $code].
+    $this->drupalGet('/render_attached_test/teapot_dpa');
+    $this->assertResponse(418);
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/teapot_dpa');
+    $this->assertResponse(418);
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Test ['#attached']['http_header'] with various replacement rules.
+    $this->drupalGet('/render_attached_test/header_dpa');
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    $this->assertTeapotHeaders();
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/header_dpa');
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Test ['#attached']['feed'].
+    $this->drupalGet('/render_attached_test/feed_dpa');
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    $this->assertFeed();
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/feed_dpa');
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+
+    // Test ['#attached']['html_head'].
+    $this->drupalGet('/render_attached_test/head_dpa');
+    $this->assertHeader('X-Drupal-Cache', 'MISS');
+    $this->assertHead();
+    // Repeat for the cache.
+    $this->drupalGet('/render_attached_test/head_dpa');
+    $this->assertHeader('X-Drupal-Cache', 'HIT');
+  }
+
+  /**
+   * Test caching of ['#attached'].
+   */
+  public function testRenderCachedBlock() {
+    // Make sure our test block is visible.
+    $this->drupalPlaceBlock('drupal_process_attached_block', ['region' => 'content']);
+
+    // Get the front page, which should now have our visible block.
+    $this->drupalGet('');
+    // Make sure our block is visible.
+    $this->assertText('Headers handled by drupal_process_attached().');
+    // Test that all our attached items are present.
+    $this->assertFeed();
+    $this->assertHead();
+    $this->assertResponse(418);
+    $this->assertTeapotHeaders();
+
+    // Reload the page, to test caching.
+    $this->drupalGet('');
+    // Make sure our block is visible.
+    $this->assertText('Headers handled by drupal_process_attached().');
+    // The header should be present again.
+    $this->assertHeader('X-Test-Teapot', 'Teapot Mode Active');
+  }
+
+  /**
+   * Helper function to make assertions about added HTTP headers.
+   */
+  protected function assertTeapotHeaders() {
+    $this->assertHeader('X-Test-Teapot', 'Teapot Mode Active');
+    $this->assertHeader('X-Test-Teapot-Replace', 'Teapot replaced');
+    $this->assertHeader('X-Test-Teapot-No-Replace', 'This value is not replaced,This one is added');
+  }
+
+  /**
+   * Helper function to make assertions about the presence of an RSS feed.
+   */
+  protected function assertFeed() {
+    // Discover the DOM element for the feed link.
+    $test_meta = $this->xpath('//head/link[@href="test://url"]');
+    $this->assertEqual(1, count($test_meta), 'Link has URL.');
+    // Reconcile the other attributes.
+    $test_meta_attributes = [
+      'href' => 'test://url',
+      'rel' => 'alternate',
+      'type' => 'application/rss+xml',
+      'title' => 'Your RSS feed.',
+    ];
+    $test_meta = reset($test_meta);
+    if (empty($test_meta)) {
+      $this->fail('Unable to find feed link.');
+    }
+    else {
+      foreach ($test_meta->attributes() as $attribute => $value) {
+        $this->assertEqual($value, $test_meta_attributes[$attribute]);
+      }
+    }
+  }
+
+  /**
+   * Helper function to make assertions about HTML head elements.
+   */
+  protected function assertHead() {
+    // Discover the DOM element for the meta link.
+    $test_meta = $this->xpath('//head/meta[@test-attribute="testvalue"]');
+    $this->assertEqual(1, count($test_meta), 'There\'s only one test attribute.');
+    // Grab the only DOM element.
+    $test_meta = reset($test_meta);
+    if (empty($test_meta)) {
+      $this->fail('Unable to find the head meta.');
+    }
+    else {
+      $test_meta_attributes = $test_meta->attributes();
+      $this->assertEqual($test_meta_attributes['test-attribute'], 'testvalue');
+    }
+  }
+
+}
diff --git a/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml b/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
new file mode 100644
index 0000000..111fb96
--- /dev/null
+++ b/core/modules/system/tests/modules/render_attached_test/render_attached_test.info.yml
@@ -0,0 +1,8 @@
+name: 'Rendering #attached test'
+type: module
+description: 'Support module for HtmlResponseAttachmentsTest.'
+package: Testing
+version: VERSION
+core: 8.x
+dependencies:
+  - block
diff --git a/core/modules/system/tests/modules/render_attached_test/render_attached_test.routing.yml b/core/modules/system/tests/modules/render_attached_test/render_attached_test.routing.yml
new file mode 100644
index 0000000..d3d578b
--- /dev/null
+++ b/core/modules/system/tests/modules/render_attached_test/render_attached_test.routing.yml
@@ -0,0 +1,55 @@
+render_attached.teapot:
+  path: '/render_attached_test/teapot'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::teapotHeaderStatus'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.header:
+  path: '/render_attached_test/header'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::header'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.head:
+  path: '/render_attached_test/head'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::head'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.feed_single:
+  path: '/render_attached_test/feed'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::feed'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.teapot_dpa:
+  path: '/render_attached_test/teapot_dpa'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::teapotHeaderStatusDpa'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.header_dpa:
+  path: '/render_attached_test/header_dpa'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::headerDpa'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.head_dpa:
+  path: '/render_attached_test/head_dpa'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::headDpa'
+  requirements:
+    _access: 'TRUE'
+
+render_attached.feed_single_dpa:
+  path: '/render_attached_test/feed_dpa'
+  defaults:
+    _controller: '\Drupal\render_attached_test\Controller\TestController::feedDpa'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/system/tests/modules/render_attached_test/src/Controller/TestController.php b/core/modules/system/tests/modules/render_attached_test/src/Controller/TestController.php
new file mode 100644
index 0000000..8e71bf0
--- /dev/null
+++ b/core/modules/system/tests/modules/render_attached_test/src/Controller/TestController.php
@@ -0,0 +1,121 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\render_attached_test\Controller\TestController.
+ */
+
+namespace Drupal\render_attached_test\Controller;
+
+/**
+ * Controller for various permutations of #attached in the render array.
+ */
+class TestController {
+
+  /**
+   * Test special header and status code rendering.
+   *
+   * @return array
+   *   A render array using features of the 'http_header' directive.
+   */
+  public function teapotHeaderStatus() {
+    $render = [];
+    $render['#attached']['http_header'][] = ['Status', "418 I'm a teapot."];
+    return $render;
+  }
+
+  /**
+   * Test attached HTML head rendering.
+   *
+   * @return array
+   *   A render array using the 'http_head' directive.
+   */
+  public function header() {
+    $render = [];
+    $render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'This value gets replaced'];
+    $render['#attached']['http_header'][] = ['X-Test-Teapot-Replace', 'Teapot replaced', TRUE];
+    $render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This value is not replaced'];
+    $render['#attached']['http_header'][] = ['X-Test-Teapot-No-Replace', 'This one is added', FALSE];
+    $render['#attached']['http_header'][] = ['X-Test-Teapot', 'Teapot Mode Active'];
+    return $render;
+  }
+
+  /**
+   * Test attached HTML head rendering.
+   *
+   * @return array
+   *   A render array using the 'html_head' directive.
+   */
+  public function head() {
+    $head = [
+      [
+        '#tag' => 'meta',
+        '#attributes' => [
+          'test-attribute' => 'testvalue',
+        ],
+      ],
+      'test_head_attribute',
+    ];
+
+    $render = [];
+    $render['#attached']['html_head'][] = $head;
+    return $render;
+  }
+
+  /**
+   * Test attached feed rendering.
+   *
+   * @return array
+   *   A render array using the 'feed' directive.
+   */
+  public function feed() {
+    $render = [];
+    $render['#attached']['feed'][] = ['test://url', 'Your RSS feed.'];
+    return $render;
+  }
+
+  /**
+   * Test special header and status code rendering as a side-effect.
+   *
+   * @return array
+   *   A generic render array.
+   */
+  public function teapotHeaderStatusDpa() {
+    \drupal_process_attached($this->teapotHeaderStatus());
+    return ['#markup' => "I'm some markup here to fool the kernel into rendering this page."];
+  }
+
+  /**
+   * Test attached HTML head rendering as a side-effect.
+   *
+   * @return array
+   *   A render array using the 'http_header' directive.
+   */
+  public function headerDpa() {
+    \drupal_process_attached($this->header());
+    return ['#markup' => "I'm some markup here to fool the kernel into rendering this page."];
+  }
+
+  /**
+   * Test attached HTML head rendering as a side-effect.
+   *
+   * @return array
+   *   A render array using the 'html_head' directive.
+   */
+  public function headDpa() {
+    \drupal_process_attached($this->head());
+    return ['#markup' => "I'm some markup here to fool the kernel into rendering this page."];
+  }
+
+  /**
+   * Test attached feed rendering as a side-effect.
+   *
+   * @return array
+   *   A render array using the 'feed' directive.
+   */
+  public function feedDpa() {
+    \drupal_process_attached($this->feed());
+    return ['#markup' => "I'm some markup here to fool the kernel into rendering this page."];
+  }
+
+}
diff --git a/core/modules/system/tests/modules/render_attached_test/src/Plugin/Block/DrupalProcessAttachedBlock.php b/core/modules/system/tests/modules/render_attached_test/src/Plugin/Block/DrupalProcessAttachedBlock.php
new file mode 100644
index 0000000..3e7b342
--- /dev/null
+++ b/core/modules/system/tests/modules/render_attached_test/src/Plugin/Block/DrupalProcessAttachedBlock.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\render_attached_test\Plugin\Block\DrupalProcessAttachedBlock.
+ */
+
+namespace Drupal\render_attached_test\Plugin\Block;
+
+use Drupal\render_attached_test\Controller\TestController;
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Render\BubbleableMetadata;
+
+/**
+ * A block we can use to test caching of #attached headers.
+ *
+ * @Block(
+ *   id = "drupal_process_attached_block",
+ *   admin_label = @Translation("DrupalProcessAttachedBlock")
+ * )
+ *
+ * @see \Drupal\system\Tests\Render\HtmlResponseAttachmentsTest
+ */
+class DrupalProcessAttachedBlock extends BlockBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    // Grab test attachment fixtures from
+    // Drupal\render_attached_test\Controller\TestController.
+    $controller = new TestController();
+    $attached = BubbleableMetadata::mergeAttachments($controller->feed(), $controller->head());
+    $attached = BubbleableMetadata::mergeAttachments($attached, $controller->header());
+    $attached = BubbleableMetadata::mergeAttachments($attached, $controller->teapotHeaderStatus());
+
+    // Use drupal_process_attached() to attach all the #attached stuff.
+    drupal_process_attached($attached);
+
+    // Return some arbitrary markup so the block doesn't disappear.
+    return ['#markup' => 'Headers handled by drupal_process_attached().'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return Cache::PERMANENT;
+  }
+
+}
