diff --git a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
index 17568d1..182f5a9 100644
--- a/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
+++ b/core/lib/Drupal/Core/Render/MainContent/HtmlRenderer.php
@@ -278,15 +278,15 @@ public function invokePageAttachmentHooks(array &$page) {
       $function = $module . '_page_attachments';
       $function($attachments);
     }
-    if (array_diff(array_keys($attachments), ['#attached', '#post_render_cache']) !== []) {
-      throw new \LogicException('Only #attached and #post_render_cache may be set in hook_page_attachments().');
+    if (array_diff(array_keys($attachments), ['#attached', '#post_render_cache', '#cache']) !== []) {
+      throw new \LogicException('Only #attached, #post_render_cache and #cache may be set in hook_page_attachments().');
     }
 
     // Modules and themes can alter page attachments.
     $this->moduleHandler->alter('page_attachments', $attachments);
     \Drupal::theme()->alter('page_attachments', $attachments);
-    if (array_diff(array_keys($attachments), ['#attached', '#post_render_cache']) !== []) {
-      throw new \LogicException('Only #attached and #post_render_cache may be set in hook_page_attachments_alter().');
+    if (array_diff(array_keys($attachments), ['#attached', '#post_render_cache', '#cache']) !== []) {
+      throw new \LogicException('Only #attached, #post_render_cache and #cache may be set in hook_page_attachments_alter().');
     }
 
     // Merge the attachments onto the $page render array.
diff --git a/core/modules/system/src/Tests/Common/PageRenderTest.php b/core/modules/system/src/Tests/Common/PageRenderTest.php
index b879f37..b76c969 100644
--- a/core/modules/system/src/Tests/Common/PageRenderTest.php
+++ b/core/modules/system/src/Tests/Common/PageRenderTest.php
@@ -54,6 +54,9 @@ function assertPageRenderHookExceptions($module, $hook) {
     $page = [];
     $html_renderer->invokePageAttachmentHooks($page);
 
+    // Assert that hooks can set cache tags.
+    $this->assertEqual($page['#cache']['tags'], ['example']);
+
     // Assert an invalid hook implementation doesn't trigger an exception.
     \Drupal::state()->set($module . '.' . $hook . '.descendant_attached', TRUE);
     $assertion = $hook . '() implementation that sets #attached on a descendant triggers an exception';
@@ -64,7 +67,7 @@ function assertPageRenderHookExceptions($module, $hook) {
     }
     catch (\LogicException $e) {
       $this->pass($assertion);
-      $this->assertEqual($e->getMessage(), 'Only #attached and #post_render_cache may be set in ' . $hook . '().');
+      $this->assertEqual($e->getMessage(), 'Only #attached, #post_render_cache and #cache may be set in ' . $hook . '().');
     }
     \Drupal::state()->set('bc_test.' . $hook . '.descendant_attached', FALSE);
 
@@ -78,7 +81,7 @@ function assertPageRenderHookExceptions($module, $hook) {
     }
     catch (\LogicException $e) {
       $this->pass($assertion);
-      $this->assertEqual($e->getMessage(), 'Only #attached and #post_render_cache may be set in ' . $hook . '().');
+      $this->assertEqual($e->getMessage(), 'Only #attached, #post_render_cache and #cache may be set in ' . $hook . '().');
     }
     \Drupal::state()->set($module . '.' . $hook . '.render_array', FALSE);
   }
diff --git a/core/modules/system/tests/modules/common_test/common_test.module b/core/modules/system/tests/modules/common_test/common_test.module
index 2323cdd..21c5261 100644
--- a/core/modules/system/tests/modules/common_test/common_test.module
+++ b/core/modules/system/tests/modules/common_test/common_test.module
@@ -224,6 +224,7 @@ function common_test_cron() {
 function common_test_page_attachments(array &$page) {
   $page['#attached']['library'][] = 'core/foo';
   $page['#attached']['library'][] = 'core/bar';
+  $page['#cache']['tags'] = ['example'];
 
   if (\Drupal::state()->get('common_test.hook_page_attachments.descendant_attached', FALSE)) {
     $page['content']['#attached']['library'][] = 'core/jquery';
@@ -248,6 +249,7 @@ function common_test_page_attachments_alter(array &$page) {
     unset($page['#attached']['library'][$index]);
   }
   $page['#attached']['library'][] = 'core/baz';
+  $page['#cache']['tags'] = ['example'];
 
   if (\Drupal::state()->get('common_test.hook_page_attachments_alter.descendant_attached', FALSE)) {
     $page['content']['#attached']['library'][] = 'core/jquery';
