diff --git a/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php b/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
new file mode 100644
index 0000000..8414154
--- /dev/null
+++ b/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Breadcrumb\Breadcrumb.
+ */
+
+namespace Drupal\Core\Breadcrumb;
+
+use Drupal\Core\Cache\CacheableMetadata;
+
+/**
+ * Used to return generated breadcrumbs, along with associated cacheability metadata.
+ */
+class Breadcrumb extends CacheableMetadata {
+
+  /**
+   * An array of links for the breadcrumb.
+   *
+   * @var \Drupal\Core\Link[]
+   */
+  protected $links = [];
+
+  /**
+   * Gets the breadcrumb links.
+   *
+   * @return \Drupal\Core\Link[]
+   */
+  public function getLinks() {
+    return $this->links ;
+  }
+
+  /**
+   * Sets the breadcrumb links.
+   *
+   * @param \Drupal\Core\Link[] $links
+   *   The breadcrumb links.
+   *
+   * @return $this
+   */
+  public function setLinks(array $links) {
+    $this->links = $links;
+
+    return $this;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
index ebdfa55..e566f54 100644
--- a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
+++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbBuilderInterface.php
@@ -32,9 +32,8 @@ public function applies(RouteMatchInterface $route_match);
    * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
    *   The current route match.
    *
-   * @return \Drupal\Core\Link[]
-   *   An array of links for the breadcrumb. Returning an empty array will
-   *   suppress all breadcrumbs.
+   * @return \Drupal\Core\Breadcrumb\Breadcrumb
+   *   A breadcrumb.
    */
   public function build(RouteMatchInterface $route_match);
 
diff --git a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
index 0099897..6cb2420 100644
--- a/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
+++ b/core/lib/Drupal/Core/Breadcrumb/BreadcrumbManager.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Breadcrumb;
 
+use \Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -88,9 +89,10 @@ public function build(RouteMatchInterface $route_match) {
 
       $build = $builder->build($route_match);
 
-      if (is_array($build)) {
+      if ($build instanceof Breadcrumb) {
         // The builder returned an array of breadcrumb links.
-        $breadcrumb = $build;
+        $breadcrumb['contexts'] = $build->getCacheContexts();
+        $breadcrumb['links'] = $build->getLinks();
         $context['builder'] = $builder;
         break;
       }
@@ -99,7 +101,7 @@ public function build(RouteMatchInterface $route_match) {
       }
     }
     // Allow modules to alter the breadcrumb.
-    $this->moduleHandler->alter('system_breadcrumb', $breadcrumb, $route_match, $context);
+    $this->moduleHandler->alter('system_breadcrumb', $breadcrumb['links'], $route_match, $context);
     // Fall back to an empty breadcrumb.
     return $breadcrumb;
   }
diff --git a/core/modules/book/src/BookBreadcrumbBuilder.php b/core/modules/book/src/BookBreadcrumbBuilder.php
index be0e63a..b1ece44 100644
--- a/core/modules/book/src/BookBreadcrumbBuilder.php
+++ b/core/modules/book/src/BookBreadcrumbBuilder.php
@@ -8,6 +8,7 @@
 namespace Drupal\book;
 
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Link;
@@ -72,6 +73,8 @@ public function applies(RouteMatchInterface $route_match) {
    */
   public function build(RouteMatchInterface $route_match) {
     $book_nids = array();
+    $breadcrumb = new Breadcrumb();
+
     $links = array(Link::createFromRoute($this->t('Home'), '<front>'));
     $book = $route_match->getParameter('node')->book;
     $depth = 1;
@@ -92,7 +95,9 @@ public function build(RouteMatchInterface $route_match) {
         $depth++;
       }
     }
-    return $links;
+    $breadcrumb->setLinks($links);
+    $breadcrumb->setCacheContexts(['route.book_navigation']);
+    return $breadcrumb;
   }
 
 }
diff --git a/core/modules/comment/src/CommentBreadcrumbBuilder.php b/core/modules/comment/src/CommentBreadcrumbBuilder.php
index 8bc2f25..94817af 100644
--- a/core/modules/comment/src/CommentBreadcrumbBuilder.php
+++ b/core/modules/comment/src/CommentBreadcrumbBuilder.php
@@ -8,6 +8,7 @@
 namespace Drupal\comment;
 
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Link;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -47,19 +48,21 @@ public function applies(RouteMatchInterface $route_match) {
    * {@inheritdoc}
    */
   public function build(RouteMatchInterface $route_match) {
-    $breadcrumb = [Link::createFromRoute($this->t('Home'), '<front>')];
+    $breadcrumb = new Breadcrumb();
+    $breadcrumb = $breadcrumb->setCacheContexts(['route']);
+    $links = [Link::createFromRoute($this->t('Home'), '<front>')];
 
     $entity = $route_match->getParameter('entity');
-    $breadcrumb[] = new Link($entity->label(), $entity->urlInfo());
+    $links[] = new Link($entity->label(), $entity->urlInfo());
 
     if (($pid = $route_match->getParameter('pid')) && ($comment = $this->storage->load($pid))) {
       /** @var \Drupal\comment\CommentInterface $comment */
       // Display link to parent comment.
       // @todo Clean-up permalink in https://www.drupal.org/node/2198041
-      $breadcrumb[] = new Link($comment->getSubject(), $comment->urlInfo());
+      $links[] = new Link($comment->getSubject(), $comment->urlInfo());
     }
 
-    return $breadcrumb;
+    return $breadcrumb->setLinks($links);
   }
 
 }
diff --git a/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php b/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php
index f595ee8..d15478f 100644
--- a/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php
+++ b/core/modules/forum/src/Breadcrumb/ForumBreadcrumbBuilderBase.php
@@ -8,6 +8,7 @@
 namespace Drupal\forum\Breadcrumb;
 
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Link;
@@ -65,14 +66,17 @@ public function __construct(EntityManagerInterface $entity_manager, ConfigFactor
    * {@inheritdoc}
    */
   public function build(RouteMatchInterface $route_match) {
-    $breadcrumb[] = Link::createFromRoute($this->t('Home'), '<front>');
+    $breadcrumb = new Breadcrumb();
+    $breadcrumb->setCacheContexts(['route']);
+
+    $links[] = Link::createFromRoute($this->t('Home'), '<front>');
 
     $vocabulary = $this->entityManager
       ->getStorage('taxonomy_vocabulary')
       ->load($this->config->get('vocabulary'));
-    $breadcrumb[] = Link::createFromRoute($vocabulary->label(), 'forum.index');
+    $links[] = Link::createFromRoute($vocabulary->label(), 'forum.index');
 
-    return $breadcrumb;
+    return $breadcrumb->setLinks($links);
   }
 
 }
diff --git a/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php b/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php
index 0e0bd04..3fb421b 100644
--- a/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php
+++ b/core/modules/forum/src/Breadcrumb/ForumNodeBreadcrumbBuilder.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\forum\Breadcrumb;
 
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Link;
 use Drupal\Core\Routing\RouteMatchInterface;
 
@@ -28,20 +29,23 @@ public function applies(RouteMatchInterface $route_match) {
    * {@inheritdoc}
    */
   public function build(RouteMatchInterface $route_match) {
-    $breadcrumb = parent::build($route_match);
+    $breadcrumb = new Breadcrumb();
+    $breadcrumb->setCacheContexts(['route']);
+    $links = parent::build($route_match);
 
     $parents = $this->forumManager->getParents($route_match->getParameter('node')->forum_tid);
     if ($parents) {
       $parents = array_reverse($parents);
       foreach ($parents as $parent) {
-        $breadcrumb[] = Link::createFromRoute($parent->label(), 'forum.page',
+        $links[] = Link::createFromRoute($parent->label(), 'forum.page',
           array(
             'taxonomy_term' => $parent->id(),
           )
         );
       }
     }
-    return $breadcrumb;
+
+    return $breadcrumb->setLinks($links);
   }
 
 }
diff --git a/core/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
index 45418a7b..c5b1ef4 100644
--- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php
+++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
@@ -9,7 +9,9 @@
 
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Controller\TitleResolverInterface;
 use Drupal\Core\Link;
@@ -125,6 +127,7 @@ public function applies(RouteMatchInterface $route_match) {
    * {@inheritdoc}
    */
   public function build(RouteMatchInterface $route_match) {
+    $breadcrumb = new Breadcrumb();
     $links = array();
 
     // General path-based breadcrumbs. Use the actual request path, prior to
@@ -139,17 +142,21 @@ public function build(RouteMatchInterface $route_match) {
     // /user is just a redirect, so skip it.
     // @todo Find a better way to deal with /user.
     $exclude['user'] = TRUE;
+    // Because this breadcrumb builder is entirely path-based, vary by the 'url'
+    // cache context.
+    $breadcrumb->setCacheContexts(['url']);
     while (count($path_elements) > 1) {
       array_pop($path_elements);
       // Copy the path elements for up-casting.
       $route_request = $this->getRequestForPath(implode('/', $path_elements), $exclude);
       if ($route_request) {
         $route_match = RouteMatch::createFromRequest($route_request);
-        $access = $this->accessManager->check($route_match, $this->currentUser);
-        if ($access) {
+        $access = $this->accessManager->check($route_match, $this->currentUser, NULL, TRUE);
+        // The set of breadcrumb links depends on the access result, so merge
+        // the access result's cacheability metadata.
+        $breadcrumb = $breadcrumb->merge(CacheableMetadata::createFromObject($access));
+        if ($access->isAllowed()) {
           $title = $this->titleResolver->getTitle($route_request, $route_match->getRouteObject());
-        }
-        if ($access) {
           if (!isset($title)) {
             // Fallback to using the raw path component as the title if the
             // route is missing a _title or _title_callback attribute.
@@ -165,7 +172,8 @@ public function build(RouteMatchInterface $route_match) {
       // Add the Home link, except for the front page.
       $links[] = Link::createFromRoute($this->t('Home'), '<front>');
     }
-    return array_reverse($links);
+
+    return $breadcrumb->setLinks(array_reverse($links));
   }
 
   /**
diff --git a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
index c7629f0..8e6a102 100644
--- a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
@@ -79,18 +79,12 @@ public function build() {
       // $breadcrumb is expected to be an array of rendered breadcrumb links.
       return array(
         '#theme' => 'breadcrumb',
-        '#links' => $breadcrumb,
+        '#links' => $breadcrumb['links'],
+        '#cache' => [
+          'contexts' => $breadcrumb['contexts'],
+        ],
       );
     }
   }
 
-  /**
-   * {@inheritdoc}
-   *
-   * @todo Make cacheable in https://www.drupal.org/node/2483183
-   */
-  public function getCacheMaxAge() {
-    return 0;
-  }
-
 }
diff --git a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
index 68e1abf..d889b96 100644
--- a/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
+++ b/core/modules/system/tests/src/Unit/Breadcrumbs/PathBasedBreadcrumbBuilderTest.php
@@ -337,9 +337,12 @@ public function testBuildWithUserPath() {
    * Setup the access manager to always allow access to routes.
    */
   public function setupAccessManagerToAllow() {
-    $this->accessManager->expects($this->any())
-      ->method('check')
+    $this->accessManager->expects($thus->any())
+      ->method('isAllowed')
       ->willReturn(TRUE);
+    $this->accessManager->expects($thus->any())
+      ->method('check')
+      ->willReturn($this->accessManager);
   }
 
   protected function setupStubPathProcessor() {
diff --git a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
index 7c4cc89..818f51d 100644
--- a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
+++ b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
@@ -8,6 +8,7 @@
 namespace Drupal\taxonomy;
 
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Link;
 use Drupal\Core\Routing\RouteMatchInterface;
@@ -67,9 +68,14 @@ public function build(RouteMatchInterface $route_match) {
       $breadcrumb[] = Link::createFromRoute($term->getName(), 'entity.taxonomy_term.canonical', array('taxonomy_term' => $term->id()));
     }
     $breadcrumb[] = Link::createFromRoute($this->t('Home'), '<front>');
-    $breadcrumb = array_reverse($breadcrumb);
+    $links = array_reverse($breadcrumb);
 
-    return $breadcrumb;
+    $breadcrumb = new Breadcrumb();
+    // This breadcrumb builder is based on a route parameter, and hence it
+    // depends on the 'route' cache context.
+    $breadcrumb->setCacheContexts(['route']);
+
+    return $breadcrumb->setLinks($links);
   }
 
 }
