diff --git a/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php b/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php
index 0a70db0633..2668025e81 100644
--- a/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php
+++ b/core/modules/layout_builder/src/Access/LayoutBuilderAccessCheck.php
@@ -4,9 +4,7 @@
 
 use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
 use Drupal\Core\Routing\Access\AccessInterface;
-use Drupal\Core\Session\AccountInterface;
 use Drupal\layout_builder\SectionStorageInterface;
-use Symfony\Component\Routing\Route;
 
 /**
  * Provides an access check for the Layout Builder defaults.
@@ -20,17 +18,12 @@ class LayoutBuilderAccessCheck implements AccessInterface {
    *
    * @param \Drupal\layout_builder\SectionStorageInterface $section_storage
    *   The section storage.
-   * @param \Drupal\Core\Session\AccountInterface $account
-   *   The current user.
-   * @param \Symfony\Component\Routing\Route $route
-   *   The route to check against.
    *
    * @return \Drupal\Core\Access\AccessResultInterface
    *   The access result.
    */
-  public function access(SectionStorageInterface $section_storage, AccountInterface $account, Route $route) {
-    $operation = $route->getRequirement('_layout_builder_access');
-    $access = $section_storage->access($operation, $account, TRUE);
+  public function access(SectionStorageInterface $section_storage) {
+    $access = $section_storage->routingAccess();
     if ($access instanceof RefinableCacheableDependencyInterface) {
       $access->addCacheableDependency($section_storage);
     }
diff --git a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
index 7f10d6f51f..fd4d85d7f3 100644
--- a/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
+++ b/core/modules/layout_builder/src/Entity/LayoutBuilderEntityViewDisplay.php
@@ -2,9 +2,12 @@
 
 namespace Drupal\layout_builder\Entity;
 
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Entity\Entity\EntityViewDisplay as BaseEntityViewDisplay;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
+use Drupal\Core\Plugin\Context\Context;
+use Drupal\Core\Plugin\Context\ContextDefinition;
 use Drupal\Core\Plugin\Context\EntityContext;
 use Drupal\Core\StringTranslation\TranslatableMarkup;
 use Drupal\field\Entity\FieldConfig;
@@ -240,7 +243,13 @@ public function buildMultiple(array $entities) {
 
     /** @var \Drupal\Core\Entity\EntityInterface $entity */
     foreach ($entities as $id => $entity) {
-      $sections = $this->getRuntimeSections($entity);
+      $cacheability = new CacheableMetadata();
+      $sections = $this->getRuntimeSections($entity, $cacheability);
+
+      // Apply cacheability metadata to the build array.
+      $build_list[$id]['_layout_builder'] = [];
+      $cacheability->applyTo($build_list[$id]['_layout_builder']);
+
       if ($sections) {
         foreach ($build_list[$id] as $name => $build_part) {
           $field_definition = $this->getFieldDefinition($name);
@@ -249,13 +258,13 @@ public function buildMultiple(array $entities) {
           }
         }
 
-        // Bypass ::getContexts() in order to use the runtime entity, not a
-        // sample entity.
-        $contexts = $this->contextRepository()->getAvailableContexts();
+        $contexts = $this->getContextsForEntity($entity);
+        // @todo Remove in https://www.drupal.org/project/drupal/issues/3018782.
         $label = new TranslatableMarkup('@entity being viewed', [
           '@entity' => $entity->getEntityType()->getSingularLabel(),
         ]);
         $contexts['layout_builder.entity'] = EntityContext::fromEntity($entity, $label);
+
         foreach ($sections as $delta => $section) {
           $build_list[$id]['_layout_builder'][$delta] = $section->toRenderArray($contexts);
         }
@@ -265,21 +274,38 @@ public function buildMultiple(array $entities) {
     return $build_list;
   }
 
+  /**
+   * Gets the available contexts for a given entity.
+   *
+   * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
+   *   The entity.
+   *
+   * @return \Drupal\Core\Plugin\Context\ContextInterface[]
+   *   An array of context objects for a given entity.
+   */
+  protected function getContextsForEntity(FieldableEntityInterface $entity) {
+    return [
+      'view_mode' => new Context(ContextDefinition::create('string'), $this->getMode()),
+      'entity' => EntityContext::fromEntity($entity),
+      'display' => EntityContext::fromEntity($this),
+    ] + $this->contextRepository()->getAvailableContexts();
+  }
+
   /**
    * Gets the runtime sections for a given entity.
    *
    * @param \Drupal\Core\Entity\FieldableEntityInterface $entity
    *   The entity.
+   * @param \Drupal\Core\Cache\CacheableMetadata|null $cacheability
+   *   (optional) Cacheability metadata object, which will be populated based on
+   *   the cacheability of each section storage candidate.
    *
    * @return \Drupal\layout_builder\Section[]
    *   The sections.
    */
-  protected function getRuntimeSections(FieldableEntityInterface $entity) {
-    if ($this->isOverridable() && !$entity->get(OverridesSectionStorage::FIELD_NAME)->isEmpty()) {
-      return $entity->get(OverridesSectionStorage::FIELD_NAME)->getSections();
-    }
-
-    return $this->getSections();
+  protected function getRuntimeSections(FieldableEntityInterface $entity, CacheableMetadata &$cacheability = NULL) {
+    $storage = $this->sectionStorageManager()->findByContext($this->getContextsForEntity($entity), $cacheability);
+    return $storage ? $storage->getSections() : [];
   }
 
   /**
@@ -399,4 +425,14 @@ protected function getDefaultSection() {
     return $this->getSection(0);
   }
 
+  /**
+   * Gets the section storage manager.
+   *
+   * @return \Drupal\layout_builder\SectionStorage\SectionStorageManagerInterface
+   *   The section storage manager.
+   */
+  private function sectionStorageManager() {
+    return \Drupal::service('plugin.manager.layout_builder.section_storage');
+  }
+
 }
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php
index f51a4653c2..656147e0db 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/DefaultsSectionStorage.php
@@ -10,7 +10,6 @@
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\Context\EntityContext;
-use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
 use Drupal\field_ui\FieldUI;
 use Drupal\layout_builder\DefaultsSectionStorageInterface;
@@ -21,8 +20,15 @@
 /**
  * Defines the 'defaults' section storage type.
  *
+ * DefaultsSectionStorage uses a positive weight because:
+ * - It must be picked after
+ *   \Drupal\layout_builder\Plugin\SectionStorage\OverridesSectionStorage.
+ * - The default weight is 0, so other custom implementations will also take
+ *   precedence unless otherwise specified.
+ *
  * @SectionStorage(
  *   id = "defaults",
+ *   weight = 20,
  *   context_definitions = {
  *     "display" = @ContextDefinition("entity:entity_view_display"),
  *   },
@@ -439,9 +445,8 @@ public function getThirdPartyProviders() {
   /**
    * {@inheritdoc}
    */
-  public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    $result = AccessResult::allowedIf($this->isLayoutBuilderEnabled());
-    return $return_as_object ? $result : $result->isAllowed();
+  public function routingAccess() {
+    return AccessResult::allowedIf($this->isLayoutBuilderEnabled())->addCacheableDependency($this);
   }
 
 }
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
index af2aca668d..1d74b90dd5 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/OverridesSectionStorage.php
@@ -9,7 +9,6 @@
 use Drupal\Core\Entity\FieldableEntityInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
 use Drupal\Core\Plugin\Context\EntityContext;
-use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Url;
 use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
 use Drupal\layout_builder\OverridesSectionStorageInterface;
@@ -329,10 +328,20 @@ public function save() {
   /**
    * {@inheritdoc}
    */
-  public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
+  public function routingAccess() {
     $default_section_storage = $this->getDefaultSectionStorage();
-    $result = AccessResult::allowedIf($default_section_storage->isLayoutBuilderEnabled())->addCacheableDependency($default_section_storage);
-    return $return_as_object ? $result : $result->isAllowed();
+    return AccessResult::allowedIf($default_section_storage->isLayoutBuilderEnabled())->addCacheableDependency($default_section_storage);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderAccess() {
+    // Check that overrides are enabled and have at least one section.
+    $default_section_storage = $this->getDefaultSectionStorage();
+    return AccessResult::allowedIf($default_section_storage->isOverridable() && count($this))
+      ->addCacheableDependency($default_section_storage)
+      ->addCacheableDependency($this->getEntity());
   }
 
 }
diff --git a/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php b/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php
index 05a2de11e3..8c9ae2737e 100644
--- a/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php
+++ b/core/modules/layout_builder/src/Plugin/SectionStorage/SectionStorageBase.php
@@ -2,7 +2,9 @@
 
 namespace Drupal\layout_builder\Plugin\SectionStorage;
 
+use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Plugin\ContextAwarePluginBase;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\layout_builder\Routing\LayoutBuilderRoutesTrait;
 use Drupal\layout_builder\Section;
 use Drupal\layout_builder\SectionListInterface;
@@ -108,4 +110,20 @@ public function getContextsDuringPreview() {
     return $this->getContexts();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
+    @trigger_error('\Drupal\layout_builder\SectionStorageInterface::access() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::routingAccess() should be used instead. See https://www.drupal.org/node/@todo.', E_USER_DEPRECATED);
+    $result = $this->routingAccess();
+    return $return_as_object ? $result : $result->isAllowed();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderAccess() {
+    return AccessResult::allowed();
+  }
+
 }
diff --git a/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php b/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php
index 0bf52bf43d..47f117ed53 100644
--- a/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php
+++ b/core/modules/layout_builder/src/SectionStorage/SectionStorageManager.php
@@ -3,6 +3,7 @@
 namespace Drupal\layout_builder\SectionStorage;
 
 use Drupal\Component\Plugin\Exception\ContextException;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\Context\ContextHandlerInterface;
@@ -92,13 +93,18 @@ public function load($type, array $contexts = []) {
   /**
    * {@inheritdoc}
    */
-  public function findByContext($operation, array $contexts) {
+  public function findByContext(array $contexts, CacheableMetadata &$cacheability = NULL) {
     $storage_types = array_keys($this->contextHandler->filterPluginDefinitionsByContexts($contexts, $this->getDefinitions()));
 
     foreach ($storage_types as $type) {
-      $plugin = $this->load($type, $contexts);
-      if ($plugin && $plugin->access($operation)) {
-        return $plugin;
+      if ($plugin = $this->load($type, $contexts)) {
+        $access = $plugin->renderAccess();
+        if ($cacheability) {
+          $cacheability->addCacheableDependency($access);
+        }
+        if ($access->isAllowed()) {
+          return $plugin;
+        }
       }
     }
     return NULL;
diff --git a/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php b/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php
index 4e09e2721a..60f08f3469 100644
--- a/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php
+++ b/core/modules/layout_builder/src/SectionStorage/SectionStorageManagerInterface.php
@@ -3,6 +3,7 @@
 namespace Drupal\layout_builder\SectionStorage;
 
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
+use Drupal\Core\Cache\CacheableMetadata;
 
 /**
  * Provides the interface for a plugin manager of section storage types.
@@ -30,15 +31,16 @@ public function load($type, array $contexts = []);
   /**
    * Finds the section storage to load based on available contexts.
    *
-   * @param string $operation
-   *   The access operation. See \Drupal\Core\Access\AccessibleInterface.
    * @param \Drupal\Component\Plugin\Context\ContextInterface[] $contexts
    *   The contexts which should be used to determine which storage to return.
+   * @param \Drupal\Core\Cache\CacheableMetadata|null $cacheability
+   *   (optional) Cacheability metadata object, which will be populated based on
+   *   the cacheability of each section storage candidate.
    *
    * @return \Drupal\layout_builder\SectionStorageInterface|null
    *   The section storage if one matched all contexts, or NULL otherwise.
    */
-  public function findByContext($operation, array $contexts);
+  public function findByContext(array $contexts, CacheableMetadata &$cacheability = NULL);
 
   /**
    * Loads a section storage with no associated section list.
diff --git a/core/modules/layout_builder/src/SectionStorageInterface.php b/core/modules/layout_builder/src/SectionStorageInterface.php
index 6dfd4793a3..52a48d8912 100644
--- a/core/modules/layout_builder/src/SectionStorageInterface.php
+++ b/core/modules/layout_builder/src/SectionStorageInterface.php
@@ -14,6 +14,8 @@
  *   Layout Builder is currently experimental and should only be leveraged by
  *   experimental modules and development releases of contributed modules.
  *   See https://www.drupal.org/core/experimental for more information.
+ *
+ * @todo Remove AccessibleInterface from this interface.
  */
 interface SectionStorageInterface extends SectionListInterface, PluginInspectionInterface, ContextAwarePluginInterface, AccessibleInterface {
 
@@ -164,4 +166,20 @@ public function label();
    */
   public function save();
 
+  /**
+   * Determines access to this plugin during routing.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  public function routingAccess();
+
+  /**
+   * Determines access to this plugin during rendering.
+   *
+   * @return \Drupal\Core\Access\AccessResultInterface
+   *   The access result.
+   */
+  public function renderAccess();
+
 }
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_overrides_test/layout_builder_overrides_test.info.yml b/core/modules/layout_builder/tests/modules/layout_builder_overrides_test/layout_builder_overrides_test.info.yml
new file mode 100644
index 0000000000..f9ed711707
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_overrides_test/layout_builder_overrides_test.info.yml
@@ -0,0 +1,6 @@
+name: 'Layout Builder overrides test'
+type: module
+description: 'Support module for testing overriding layout building.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_overrides_test/src/Plugin/SectionStorage/TestOverridesSectionStorage.php b/core/modules/layout_builder/tests/modules/layout_builder_overrides_test/src/Plugin/SectionStorage/TestOverridesSectionStorage.php
new file mode 100644
index 0000000000..39d33b13dc
--- /dev/null
+++ b/core/modules/layout_builder/tests/modules/layout_builder_overrides_test/src/Plugin/SectionStorage/TestOverridesSectionStorage.php
@@ -0,0 +1,99 @@
+<?php
+
+namespace Drupal\layout_builder_overrides_test\Plugin\SectionStorage;
+
+use Drupal\Core\Access\AccessResult;
+use Drupal\layout_builder\Plugin\SectionStorage\SectionStorageBase;
+use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
+use Symfony\Component\Routing\RouteCollection;
+
+/**
+ * Provides a test override of section storage.
+ *
+ * @SectionStorage(
+ *   id = "layout_builder_overrides_test",
+ *   weight = -100,
+ * )
+ */
+class TestOverridesSectionStorage extends SectionStorageBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSections() {
+    // Return a custom section.
+    $section = new Section('layout_onecol');
+    $section->appendComponent(new SectionComponent('fake-uuid', 'content', [
+      'id' => 'system_powered_by_block',
+      'label' => 'Test block title',
+      'label_display' => 'visible',
+    ]));
+    return [$section];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function routingAccess() {
+    return AccessResult::allowed();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderAccess() {
+    return AccessResult::allowedIf(\Drupal::state()->get('layout_builder_overrides_test', FALSE))->setCacheMaxAge(0);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getSectionList() {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getStorageId() {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSectionListFromId($id) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildRoutes(RouteCollection $collection) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRedirectUrl() {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLayoutBuilderUrl($rel = 'view') {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function extractIdFromRoute($value, $definition, $name, array $defaults) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function deriveContextsFromRoute($value, $definition, $name, array $defaults) {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function label() {}
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save() {}
+
+}
diff --git a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php
index c92bb2d1ed..2dbae56b3a 100644
--- a/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php
+++ b/core/modules/layout_builder/tests/modules/layout_builder_test/src/Plugin/SectionStorage/SimpleConfigSectionStorage.php
@@ -186,10 +186,25 @@ public function getRedirectUrl() {
    * {@inheritdoc}
    */
   public function access($operation, AccountInterface $account = NULL, $return_as_object = FALSE) {
-    $result = AccessResult::allowed();
+    @trigger_error('\Drupal\layout_builder\SectionStorageInterface::access() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::routingAccess() should be used instead. See https://www.drupal.org/node/@todo.', E_USER_DEPRECATED);
+    $result = $this->routingAccess();
     return $return_as_object ? $result : $result->isAllowed();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function routingAccess() {
+    return AccessResult::allowed();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function renderAccess() {
+    return AccessResult::allowed();
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/layout_builder/tests/src/Functional/LayoutBuilderSectionStorageTest.php b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderSectionStorageTest.php
new file mode 100644
index 0000000000..07f0ea0162
--- /dev/null
+++ b/core/modules/layout_builder/tests/src/Functional/LayoutBuilderSectionStorageTest.php
@@ -0,0 +1,89 @@
+<?php
+
+namespace Drupal\Tests\layout_builder\Functional;
+
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the UI aspects of section storage.
+ *
+ * @group layout_builder
+ */
+class LayoutBuilderSectionStorageTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = [
+    'layout_builder',
+    'node',
+    'layout_builder_test',
+    'layout_builder_overrides_test',
+  ];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    // @todo The Layout Builder UI relies on local tasks; fix in
+    //   https://www.drupal.org/project/drupal/issues/2917777.
+    $this->drupalPlaceBlock('local_tasks_block');
+
+    // Create two nodes.
+    $this->createContentType(['type' => 'bundle_with_section_field']);
+    $this->createNode([
+      'type' => 'bundle_with_section_field',
+      'title' => 'The first node title',
+      'body' => [
+        [
+          'value' => 'The first node body',
+        ],
+      ],
+    ]);
+  }
+
+  /**
+   * Tests that section loading is delegated to plugins during rendering.
+   *
+   * @see \Drupal\layout_builder_overrides_test\Plugin\SectionStorage\TestOverridesSectionStorage
+   */
+  public function testRenderByContextAwarePluginDelegate() {
+    $assert_session = $this->assertSession();
+    $page = $this->getSession()->getPage();
+
+    $this->drupalLogin($this->drupalCreateUser([
+      'configure any layout',
+      'administer node display',
+    ]));
+
+    // No blocks exist on the node by default.
+    $this->drupalGet('node/1');
+    $assert_session->pageTextNotContains('Defaults block title');
+    $assert_session->pageTextNotContains('Test block title');
+
+    // Enable Layout Builder.
+    $this->drupalPostForm('admin/structure/types/manage/bundle_with_section_field/display/default', ['layout[enabled]' => TRUE], 'Save');
+
+    // Add a block to the defaults.
+    $page->clickLink('Manage layout');
+    $page->clickLink('Add Block');
+    $page->clickLink('Powered by Drupal');
+    $page->fillField('settings[label]', 'Defaults block title');
+    $page->checkField('settings[label_display]');
+    $page->pressButton('Add Block');
+    $page->clickLink('Save Layout');
+
+    $this->drupalGet('node/1');
+    $assert_session->pageTextContains('Defaults block title');
+    $assert_session->pageTextNotContains('Test block title');
+
+    // Enable the test section storage.
+    $this->container->get('state')->set('layout_builder_overrides_test', TRUE);
+    $this->drupalGet('node/1');
+    $assert_session->pageTextNotContains('Defaults block title');
+    $assert_session->pageTextContains('Test block title');
+  }
+
+}
diff --git a/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php b/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php
index 5e3e1a2cb5..dde1577b27 100644
--- a/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/DefaultsSectionStorageTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\layout_builder\Kernel;
 
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultInterface;
 use Drupal\Core\Plugin\Context\EntityContext;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\KernelTests\KernelTestBase;
@@ -55,6 +57,9 @@ protected function setUp() {
    * @covers ::access
    * @dataProvider providerTestAccess
    *
+   * @group legacy
+   * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::access() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::routingAccess() should be used instead. See https://www.drupal.org/node/@todo.
+   *
    * @param bool $expected
    *   The expected outcome of ::access().
    * @param string $operation
@@ -106,6 +111,74 @@ public function providerTestAccess() {
     return $data;
   }
 
+  /**
+   * @covers ::routingAccess
+   * @dataProvider providerTestRoutingAccess
+   *
+   * @param \Drupal\Core\Access\AccessResultInterface $expected
+   *   The expected outcome of ::routingAccess().
+   * @param bool $is_enabled
+   *   Whether Layout Builder is enabled for this display.
+   * @param array $section_data
+   *   Data to store as the sections value for Layout Builder.
+   */
+  public function testRoutingAccess(AccessResultInterface $expected, $is_enabled, array $section_data) {
+    $display = LayoutBuilderEntityViewDisplay::create([
+      'targetEntityType' => 'entity_test',
+      'bundle' => 'entity_test',
+      'mode' => 'default',
+      'status' => TRUE,
+    ]);
+    if ($is_enabled) {
+      $display->enableLayoutBuilder();
+    }
+    $display
+      ->setThirdPartySetting('layout_builder', 'sections', $section_data)
+      ->save();
+
+    $this->plugin->setContext('display', EntityContext::fromEntity($display));
+    $result = $this->plugin->routingAccess();
+    $this->assertEquals($expected, $result);
+  }
+
+  /**
+   * Provides test data for ::testRoutingAccess().
+   */
+  public function providerTestRoutingAccess() {
+    $section_data = [
+      new Section('layout_onecol', [], [
+        'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
+      ]),
+    ];
+
+    // Data provider values are:
+    // - the expected outcome of the call to ::routingAccess()
+    // - whether Layout Builder has been enabled for this display
+    // - whether this display has any section data.
+    $data = [];
+    $data['disabled, no data'] = [
+      AccessResult::neutral()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      FALSE,
+      [],
+    ];
+    $data['enabled, no data'] = [
+      AccessResult::allowed()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      TRUE,
+      [],
+    ];
+    $data['disabled, data'] = [
+      AccessResult::neutral()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      FALSE,
+      $section_data,
+    ];
+    $data['enabled, data'] = [
+      AccessResult::allowed()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      TRUE,
+      $section_data,
+    ];
+    return $data;
+  }
+
   /**
    * @covers ::getContexts
    */
diff --git a/core/modules/layout_builder/tests/src/Kernel/OverridesSectionStorageTest.php b/core/modules/layout_builder/tests/src/Kernel/OverridesSectionStorageTest.php
index cb8a0d35d7..dade9f2ca8 100644
--- a/core/modules/layout_builder/tests/src/Kernel/OverridesSectionStorageTest.php
+++ b/core/modules/layout_builder/tests/src/Kernel/OverridesSectionStorageTest.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Tests\layout_builder\Kernel;
 
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultInterface;
 use Drupal\Core\Plugin\Context\EntityContext;
 use Drupal\entity_test\Entity\EntityTest;
 use Drupal\KernelTests\KernelTestBase;
@@ -54,6 +56,9 @@ protected function setUp() {
    * @covers ::access
    * @dataProvider providerTestAccess
    *
+   * @group legacy
+   * @expectedDeprecation \Drupal\layout_builder\SectionStorageInterface::access() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. \Drupal\layout_builder\SectionStorageInterface::routingAccess() should be used instead. See https://www.drupal.org/node/@todo.
+   *
    * @param bool $expected
    *   The expected outcome of ::access().
    * @param string $operation
@@ -108,6 +113,77 @@ public function providerTestAccess() {
     return $data;
   }
 
+  /**
+   * @covers ::routingAccess
+   * @dataProvider providerTestRoutingAccess
+   *
+   * @param \Drupal\Core\Access\AccessResultInterface $expected
+   *   The expected outcome of ::routingAccess().
+   * @param bool $is_enabled
+   *   Whether Layout Builder is enabled for this display.
+   * @param array $section_data
+   *   Data to store as the sections value for Layout Builder.
+   */
+  public function testRoutingAccess(AccessResultInterface $expected, $is_enabled, array $section_data) {
+    $display = LayoutBuilderEntityViewDisplay::create([
+      'targetEntityType' => 'entity_test',
+      'bundle' => 'entity_test',
+      'mode' => 'default',
+      'status' => TRUE,
+    ]);
+    if ($is_enabled) {
+      $display->enableLayoutBuilder();
+    }
+    $display
+      ->setOverridable()
+      ->save();
+
+    $entity = EntityTest::create([OverridesSectionStorage::FIELD_NAME => $section_data]);
+    $entity->save();
+
+    $this->plugin->setContext('entity', EntityContext::fromEntity($entity));
+    $result = $this->plugin->routingAccess();
+    $this->assertEquals($expected, $result);
+  }
+
+  /**
+   * Provides test data for ::testRoutingAccess().
+   */
+  public function providerTestRoutingAccess() {
+    $section_data = [
+      new Section('layout_default', [], [
+        'first-uuid' => new SectionComponent('first-uuid', 'content', ['id' => 'foo']),
+      ]),
+    ];
+
+    // Data provider values are:
+    // - the expected outcome of the call to ::routingAccess()
+    // - whether Layout Builder has been enabled for this display
+    // - whether this display has any section data.
+    $data = [];
+    $data['disabled, no data'] = [
+      AccessResult::neutral()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      FALSE,
+      [],
+    ];
+    $data['enabled, no data'] = [
+      AccessResult::allowed()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      TRUE,
+      [],
+    ];
+    $data['disabled, data'] = [
+      AccessResult::neutral()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      FALSE,
+      $section_data,
+    ];
+    $data['enabled, data'] = [
+      AccessResult::allowed()->addCacheTags(['config:core.entity_view_display.entity_test.entity_test.default']),
+      TRUE,
+      $section_data,
+    ];
+    return $data;
+  }
+
   /**
    * @covers ::getContexts
    */
diff --git a/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php b/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php
index 155799c9e0..93749a49c5 100644
--- a/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php
+++ b/core/modules/layout_builder/tests/src/Unit/SectionStorageManagerTest.php
@@ -6,6 +6,9 @@
 use Drupal\Component\Plugin\Discovery\DiscoveryInterface;
 use Drupal\Component\Plugin\Exception\ContextException;
 use Drupal\Component\Plugin\Factory\FactoryInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultInterface;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Plugin\Context\Context;
@@ -203,10 +206,12 @@ public function testFindDefinitions() {
    *
    * @dataProvider providerTestFindByContext
    *
-   * @param bool $plugin_access
+   * @param \Drupal\Core\Access\AccessResultInterface $plugin_access
    *   The result for the plugin's access method to return.
+   * @param bool $boolean_access
+   *   The Boolean equivalent of $plugin_access.
    */
-  public function testFindByContext($plugin_access) {
+  public function testFindByContext(AccessResultInterface $plugin_access, $boolean_access) {
     $contexts = [
       'foo' => new Context(new ContextDefinition('foo')),
     ];
@@ -218,10 +223,10 @@ public function testFindByContext($plugin_access) {
     $this->discovery->getDefinitions()->willReturn($definitions);
 
     $provider_access = $this->prophesize(SectionStorageInterface::class);
-    $provider_access->access('test_operation')->willReturn($plugin_access);
+    $provider_access->renderAccess()->willReturn($plugin_access);
 
     $no_access = $this->prophesize(SectionStorageInterface::class);
-    $no_access->access('test_operation')->willReturn(FALSE);
+    $no_access->renderAccess()->willReturn(AccessResult::neutral());
 
     $missing_contexts = $this->prophesize(SectionStorageInterface::class);
 
@@ -235,8 +240,8 @@ public function testFindByContext($plugin_access) {
     $this->factory->createInstance('missing_contexts', [])->willReturn($missing_contexts->reveal());
     $this->factory->createInstance('provider_access', [])->willReturn($provider_access->reveal());
 
-    $result = $this->manager->findByContext('test_operation', $contexts);
-    if ($plugin_access) {
+    $result = $this->manager->findByContext($contexts);
+    if ($boolean_access) {
       $this->assertSame($provider_access->reveal(), $result);
     }
     else {
@@ -250,10 +255,45 @@ public function testFindByContext($plugin_access) {
   public function providerTestFindByContext() {
     // Data provider values are:
     // - the result for the plugin's access method to return.
+    // - the Boolean equivalent of that result.
     $data = [];
-    $data['plugin access: true'] = [TRUE];
-    $data['plugin access: false'] = [FALSE];
+    $data['plugin access: true'] = [AccessResult::allowed(), TRUE];
+    $data['plugin access: false'] = [AccessResult::neutral(), FALSE];
     return $data;
   }
 
+  /**
+   * @covers ::findByContext
+   */
+  public function testFindByContextCacheableSectionStorage() {
+    $contexts = [
+      'foo' => new Context(new ContextDefinition('foo')),
+    ];
+
+    $definitions = [
+      'first' => new SectionStorageDefinition(),
+      'second' => new SectionStorageDefinition(),
+    ];
+    $this->discovery->getDefinitions()->willReturn($definitions);
+
+    $first_plugin = $this->prophesize(SectionStorageInterface::class);
+    $first_plugin->renderAccess()->willReturn(AccessResult::neutral()->addCacheTags(['first_plugin']));
+
+    $second_plugin = $this->prophesize(SectionStorageInterface::class);
+    $second_plugin->renderAccess()->willReturn(AccessResult::allowed()->addCacheTags(['second_plugin']));
+
+    $this->factory->createInstance('first', [])->willReturn($first_plugin->reveal());
+    $this->factory->createInstance('second', [])->willReturn($second_plugin->reveal());
+
+    // Do not do any filtering based on context.
+    $this->contextHandler->filterPluginDefinitionsByContexts($contexts, $definitions)->willReturnArgument(1);
+    $this->contextHandler->applyContextMapping($first_plugin, $contexts)->shouldBeCalled();
+    $this->contextHandler->applyContextMapping($second_plugin, $contexts)->shouldBeCalled();
+
+    $cacheability = new CacheableMetadata();
+    $result = $this->manager->findByContext($contexts, $cacheability);
+    $this->assertSame($second_plugin->reveal(), $result);
+    $this->assertSame(['first_plugin', 'second_plugin'], $cacheability->getCacheTags());
+  }
+
 }
