 core/lib/Drupal/Core/Render/Renderer.php           |  2 +-
 core/lib/Drupal/Core/Render/RendererInterface.php  |  3 +-
 core/modules/block/src/BlockViewBuilder.php        |  6 +---
 .../Drupal/Tests/Core/Render/RendererTest.php      | 40 ++++++++++++++++++++++
 4 files changed, 44 insertions(+), 7 deletions(-)

diff --git a/core/lib/Drupal/Core/Render/Renderer.php b/core/lib/Drupal/Core/Render/Renderer.php
index a7f7346..390de66 100644
--- a/core/lib/Drupal/Core/Render/Renderer.php
+++ b/core/lib/Drupal/Core/Render/Renderer.php
@@ -546,7 +546,7 @@ protected function cacheSet(array &$elements, $pre_bubbling_cid) {
     $data = $this->getCacheableRenderArray($elements);
 
     $bin = isset($elements['#cache']['bin']) ? $elements['#cache']['bin'] : 'render';
-    $expire = isset($elements['#cache']['expire']) ? $elements['#cache']['expire'] : Cache::PERMANENT;
+    $expire = ($elements['#cache']['max-age'] === Cache::PERMANENT) ? Cache::PERMANENT : REQUEST_TIME + $elements['#cache']['max-age'];
     $cache = $this->cacheFactory->get($bin);
 
     // Two-tier caching: detect different CID post-bubbling, create redirect,
diff --git a/core/lib/Drupal/Core/Render/RendererInterface.php b/core/lib/Drupal/Core/Render/RendererInterface.php
index 33502ba..87e0acf 100644
--- a/core/lib/Drupal/Core/Render/RendererInterface.php
+++ b/core/lib/Drupal/Core/Render/RendererInterface.php
@@ -117,10 +117,11 @@ public function renderPlain(&$elements);
    *     - 'contexts': An array of one or more cache context IDs. These are
    *       converted to a final value depending on the request. (e.g. 'user' is
    *       mapped to the current user's ID.)
+   *     - 'max-age': A time in seconds. Zero seconds means it is not cacheable.
+   *       \Drupal\Core\Cache\Cache::PERMANENT means it is cacheable forever.
    *     - 'cid': Specify the cache ID directly. Either 'keys' or 'cid' is
    *       required. If 'cid' is set, 'keys' is ignored. Use only if you have
    *       special requirements.
-   *     - 'expire': Set to one of the cache lifetime constants.
    *     - 'bin': Specify a cache bin to cache the element in. Default is
    *       'default'.
    *     When there is a render cache hit, there is no rendering work left to be
diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index b904686..1c1ce7b 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -91,11 +91,7 @@ public function viewMultiple(array $entities = array(), $view_mode = 'full', $la
           'block',
           $entity->id(),
         );
-        $max_age = $plugin->getCacheMaxAge();
-        $build[$entity_id]['#cache'] += array(
-          'keys' => array_merge($default_cache_keys, $plugin->getCacheKeys()),
-          'expire' => ($max_age === Cache::PERMANENT) ? Cache::PERMANENT : REQUEST_TIME + $max_age,
-        );
+        $build[$entity_id]['#cache']['keys'] = array_merge($default_cache_keys, $plugin->getCacheKeys());
       }
       else {
         $build[$entity_id] = $this->buildBlock($build[$entity_id]);
diff --git a/core/tests/Drupal/Tests/Core/Render/RendererTest.php b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
index a53155f..b499753 100644
--- a/core/tests/Drupal/Tests/Core/Render/RendererTest.php
+++ b/core/tests/Drupal/Tests/Core/Render/RendererTest.php
@@ -577,6 +577,46 @@ public function testRenderCache() {
     $this->assertSame(Cache::mergeTags($expected_tags, ['rendered']), $cache_item->tags);
   }
 
+  /**
+   * @covers ::render
+   * @covers ::doRender
+   * @covers ::cacheGet
+   * @covers ::cacheSet
+   * @covers ::createCacheID
+   *
+   * @dataProvider providerTestRenderCacheMaxAge
+   */
+  public function testRenderCacheMaxAge($max_age, $is_render_cached, $render_cache_item_expire) {
+    $this->setUpRequest();
+    $this->setupMemoryCache();
+
+    $element = [
+      '#cache' => [
+        'cid' => 'render_cache_test',
+        'max-age' => $max_age,
+      ],
+      '#markup' => '',
+    ];
+    $this->renderer->render($element);
+
+    $cache_item = $this->cacheFactory->get('render')->get('render_cache_test');
+    if (!$is_render_cached) {
+      $this->assertFalse($cache_item);
+    }
+    else {
+      $this->assertNotFalse($cache_item);
+      $this->assertSame($render_cache_item_expire, $cache_item->expire);
+    }
+  }
+
+  public function providerTestRenderCacheMaxAge() {
+    return [
+      [0, FALSE, NULL],
+      [60, TRUE, REQUEST_TIME + 60],
+      [Cache::PERMANENT, TRUE, -1],
+    ];
+  }
+
 }
 
 class TestAccessClass {
