 core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php     | 46 ++++++++++++++++++++++
 .../Core/Breadcrumb/BreadcrumbBuilderInterface.php |  5 +--
 .../system/src/PathBasedBreadcrumbBuilder.php      | 17 +++++---
 .../src/Plugin/Block/SystemBreadcrumbBlock.php     |  9 -----
 .../modules/taxonomy/src/TermBreadcrumbBuilder.php |  8 +++-
 5 files changed, 67 insertions(+), 18 deletions(-)

diff --git a/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php b/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
index e69de29..4dda5be 100644
--- a/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
+++ b/core/lib/Drupal/Core/Breadcrumb/Breadcrumb.php
@@ -0,0 +1,46 @@
+<?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/modules/system/src/PathBasedBreadcrumbBuilder.php b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
index 45418a7b..d19eddd2 100644
--- a/core/modules/system/src/PathBasedBreadcrumbBuilder.php
+++ b/core/modules/system/src/PathBasedBreadcrumbBuilder.php
@@ -10,6 +10,7 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Access\AccessManagerInterface;
 use Drupal\Core\Breadcrumb\BreadcrumbBuilderInterface;
+use Drupal\Core\Breadcrumb\Breadcrumb;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Controller\TitleResolverInterface;
 use Drupal\Core\Link;
@@ -125,6 +126,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 +141,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($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 +171,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..e822a72 100644
--- a/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
+++ b/core/modules/system/src/Plugin/Block/SystemBreadcrumbBlock.php
@@ -84,13 +84,4 @@ public function build() {
     }
   }
 
-  /**
-   * {@inheritdoc}
-   *
-   * @todo Make cacheable in https://www.drupal.org/node/2483183
-   */
-  public function getCacheMaxAge() {
-    return 0;
-  }
-
 }
diff --git a/core/modules/taxonomy/src/TermBreadcrumbBuilder.php b/core/modules/taxonomy/src/TermBreadcrumbBuilder.php
index 7c4cc89..bbcfd49 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,8 +68,13 @@ 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);
 
+    $breadcrumb = new Breadcrumb();
+    // This breadcrumb builder is based on a route parameter, and hence it
+    // depends on the 'route' cache context.
+    $breadcrumb->setCacheContexts(['route']);
+    $breadcrumb->setLinks($links);
     return $breadcrumb;
   }
 
