diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableAccessDeniedHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableAccessDeniedHttpException.php
new file mode 100644
index 0000000..49750ec
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableAccessDeniedHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
+
+/**
+ * An Access Denied
+ * exception that contains and can expose cacheability metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableAccessDeniedHttpException extends AccessDeniedHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableBadRequestHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableBadRequestHttpException.php
new file mode 100644
index 0000000..d6ead4d
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableBadRequestHttpException.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+
+/**
+ * A Bad Request exception that contains and can expose cacheability metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableBadRequestHttpException extends BadRequestHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableConflictHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableConflictHttpException.php
new file mode 100644
index 0000000..69b638b
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableConflictHttpException.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\ConflictHttpException;
+
+/**
+ * A Conflict exception that contains and can expose cacheability metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableConflictHttpException extends ConflictHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableGoneHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableGoneHttpException.php
new file mode 100644
index 0000000..f9c0c51
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableGoneHttpException.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\GoneHttpException;
+
+/**
+ * A Gone exception that contains and can expose cacheability metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableGoneHttpException extends GoneHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableHttpException.php
new file mode 100644
index 0000000..599d7c1
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableHttpException.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\HttpException;
+
+/**
+ * An exception that contains and can expose cacheability metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableHttpException extends HttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableHttpExceptionInterface.php b/core/lib/Drupal/Core/Cache/Exception/CacheableHttpExceptionInterface.php
new file mode 100644
index 0000000..44dfe4b
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableHttpExceptionInterface.php
@@ -0,0 +1,12 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseInterface;
+
+/**
+ * Defines an interface for exception that can expose cacheability metadata.
+ *
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+interface CacheableHttpExceptionInterface extends CacheableResponseInterface {}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableLengthRequiredHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableLengthRequiredHttpException.php
new file mode 100644
index 0000000..11bc935
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableLengthRequiredHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\LengthRequiredHttpException;
+
+/**
+ * A Length Required exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableLengthRequiredHttpException extends LengthRequiredHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableMethodNotAllowedHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableMethodNotAllowedHttpException.php
new file mode 100644
index 0000000..1b1d07b
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableMethodNotAllowedHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
+
+/**
+ * A Method Not Allowed exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableMethodNotAllowedHttpException extends MethodNotAllowedHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableNotAcceptableHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableNotAcceptableHttpException.php
new file mode 100644
index 0000000..e12a070
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableNotAcceptableHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
+
+/**
+ * A Not Acceptable exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableNotAcceptableHttpException extends NotAcceptableHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableNotFoundHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableNotFoundHttpException.php
new file mode 100644
index 0000000..98688f2
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableNotFoundHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
+
+/**
+ * A Not Found exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableNotFoundHttpException extends NotFoundHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheablePreconditionFailedHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheablePreconditionFailedHttpException.php
new file mode 100644
index 0000000..3a6d5c7
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheablePreconditionFailedHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\PreconditionFailedHttpException;
+
+/**
+ * A Precondition Failed exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheablePreconditionFailedHttpException extends PreconditionFailedHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheablePreconditionRequiredHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheablePreconditionRequiredHttpException.php
new file mode 100644
index 0000000..44955c8
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheablePreconditionRequiredHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\PreconditionRequiredHttpException;
+
+/**
+ * A Precondition Required exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheablePreconditionRequiredHttpException extends PreconditionRequiredHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableServiceUnavailableHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableServiceUnavailableHttpException.php
new file mode 100644
index 0000000..f2b92a9
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableServiceUnavailableHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\ServiceUnavailableHttpException;
+
+/**
+ * A Service Unavailable exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableServiceUnavailableHttpException extends ServiceUnavailableHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableTooManyRequestsHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableTooManyRequestsHttpException.php
new file mode 100644
index 0000000..d8620c9
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableTooManyRequestsHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
+
+/**
+ * A Too Many Requests exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableTooManyRequestsHttpException extends TooManyRequestsHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableUnauthorizedHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableUnauthorizedHttpException.php
new file mode 100644
index 0000000..ff250f5
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableUnauthorizedHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
+
+/**
+ * An Unauthorized exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableUnauthorizedHttpException extends UnauthorizedHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableUnprocessableEntityHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableUnprocessableEntityHttpException.php
new file mode 100644
index 0000000..140d333
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableUnprocessableEntityHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\UnprocessableEntityHttpException;
+
+/**
+ * An Unprocessable Entity exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableUnprocessableEntityHttpException extends UnprocessableEntityHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Cache/Exception/CacheableUnsupportedMediaTypeHttpException.php b/core/lib/Drupal/Core/Cache/Exception/CacheableUnsupportedMediaTypeHttpException.php
new file mode 100644
index 0000000..dc9ffd1
--- /dev/null
+++ b/core/lib/Drupal/Core/Cache/Exception/CacheableUnsupportedMediaTypeHttpException.php
@@ -0,0 +1,23 @@
+<?php
+
+namespace Drupal\Core\Cache\Exception;
+
+use Drupal\Core\Cache\CacheableResponseTrait;
+use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
+
+/**
+ * An Unsupported Media Type exception that contains and can expose cacheability
+ * metadata.
+ *
+ * Supports Drupal's caching concepts: cache tags for invalidation and cache
+ * contexts for variations.
+ *
+ * @see \Drupal\Core\Cache\Cache
+ * @see \Drupal\Core\Cache\CacheableMetadata
+ * @see \Drupal\Core\Cache\CacheableResponseTrait
+ */
+class CacheableUnsupportedMediaTypeHttpException extends UnsupportedMediaTypeHttpException implements CacheableHttpExceptionInterface  {
+
+  use CacheableResponseTrait;
+
+}
diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 2c6dbc1..d4e203d 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -527,10 +527,8 @@ protected function invalidateTagsOnSave($update) {
     // listing's filtering requirements. A newly created entity may start to
     // appear in listings because it did not exist before.)
     $tags = $this->getEntityType()->getListCacheTags();
-    if ($this->hasLinkTemplate('canonical')) {
-      // Creating or updating an entity may change a cached 403 or 404 response.
-      $tags = Cache::mergeTags($tags, ['4xx-response']);
-    }
+    // Creating or updating an entity may change a cached 403 or 404 response.
+    $tags = Cache::mergeTags($tags, ['4xx-response']);
     if ($update) {
       // An existing entity was updated, also invalidate its unique cache tag.
       $tags = Cache::mergeTags($tags, $this->getCacheTagsToInvalidate());
diff --git a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
index 62f5486..3ce3ef2 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
@@ -5,6 +5,8 @@
 use Drupal\Core\Authentication\AuthenticationProviderFilterInterface;
 use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
 use Drupal\Core\Authentication\AuthenticationProviderInterface;
+use Drupal\Core\Cache\Exception\CacheableAccessDeniedHttpException;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
 use Drupal\Core\Session\AccountProxyInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -96,7 +98,7 @@ public function onKernelRequestFilterProvider(GetResponseEvent $event) {
     if (isset($this->filter) && $event->getRequestType() === HttpKernelInterface::MASTER_REQUEST) {
       $request = $event->getRequest();
       if ($this->authenticationProvider->applies($request) && !$this->filter->appliesToRoutedRequest($request, TRUE)) {
-        throw new AccessDeniedHttpException();
+        throw new CacheableAccessDeniedHttpException();
       }
     }
   }
@@ -118,6 +120,9 @@ public function onExceptionSendChallenge(GetResponseForExceptionEvent $event) {
       if ($exception instanceof AccessDeniedHttpException && !$this->authenticationProvider->applies($request) && (!isset($this->filter) || $this->filter->appliesToRoutedRequest($request, FALSE))) {
         $challenge_exception = $this->challengeProvider->challengeException($request, $exception);
         if ($challenge_exception) {
+          if ($exception instanceof CacheableHttpExceptionInterface && $challenge_exception instanceof CacheableHttpExceptionInterface) {
+            $challenge_exception->addCacheableDependency($exception->getCacheableMetadata());
+          }
           $event->setException($challenge_exception);
         }
       }
diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
index 38cb895..76528d9 100644
--- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionHtmlSubscriber.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\Core\EventSubscriber;
 
+use Drupal\Core\Cache\CacheableResponseInterface;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
 use Drupal\Core\Routing\RedirectDestinationInterface;
 use Drupal\Core\Utility\Error;
 use Psr\Log\LoggerInterface;
@@ -158,6 +160,11 @@ protected function makeSubrequest(GetResponseForExceptionEvent $event, $url, $st
         $response->setStatusCode($status_code);
       }
 
+      // Persist any cacheability metadata.
+      if ($response instanceof CacheableResponseInterface && $exception instanceof CacheableHttpExceptionInterface) {
+        $response->addCacheableDependency($exception->getCacheableMetadata());
+      }
+
       // Persist any special HTTP headers that were set on the exception.
       if ($exception instanceof HttpExceptionInterface) {
         $response->headers->add($exception->getHeaders());
diff --git a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
index 4737e80..ba0418b 100644
--- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
@@ -3,11 +3,13 @@
 namespace Drupal\Core\EventSubscriber;
 
 use Drupal\Component\Utility\SafeMarkup;
+use Drupal\Core\Cache\CacheableJsonResponse;
+use Drupal\Core\Cache\CacheableResponse;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\StringTranslation\StringTranslationTrait;
 use Drupal\Core\Utility\Error;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
@@ -119,7 +121,13 @@ protected function onHtml(GetResponseForExceptionEvent $event) {
 
     $content = $this->t('The website encountered an unexpected error. Please try again later.');
     $content .= $message ? '</br></br>' . $message : '';
-    $response = new Response($content, 500);
+    $response = new CacheableResponse($content, 500);
+    $response->prepare($event->getRequest());
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
 
     if ($exception instanceof HttpExceptionInterface) {
       $response->setStatusCode($exception->getStatusCode());
@@ -151,7 +159,14 @@ protected function onJson(GetResponseForExceptionEvent $event) {
       $data = ['message' => sprintf('A fatal error occurred: %s', $message)];
     }
 
-    $response = new JsonResponse($data, Response::HTTP_INTERNAL_SERVER_ERROR);
+    $response = new CacheableJsonResponse($data, Response::HTTP_INTERNAL_SERVER_ERROR);
+    $response->prepare($event->getRequest());
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+
     if ($exception instanceof HttpExceptionInterface) {
       $response->setStatusCode($exception->getStatusCode());
       $response->headers->add($exception->getHeaders());
@@ -170,7 +185,14 @@ protected function onFormatUnknown(GetResponseForExceptionEvent $event) {
     /** @var \Symfony\Component\HttpKernel\Exception\HttpExceptionInterface|\Exception $exception */
     $exception = $event->getException();
 
-    $response = new Response($exception->getMessage(), $exception->getStatusCode(), $exception->getHeaders());
+    $response = new CacheableResponse($exception->getMessage(), $exception->getStatusCode(), $exception->getHeaders());
+    $response->prepare($event->getRequest());
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+
     $event->setResponse($response);
   }
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
index 2853b98..92fb309 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
@@ -2,9 +2,11 @@
 
 namespace Drupal\Core\EventSubscriber;
 
-use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\Core\Cache\CacheableJsonResponse;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 
 /**
  * Default handling for JSON errors.
@@ -34,8 +36,17 @@ protected static function getPriority() {
    *   The event to process.
    */
   public function on400(GetResponseForExceptionEvent $event) {
-    $response = new JsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_BAD_REQUEST);
-    $event->setResponse($response);
+    $this->setEventResponse($event, Response::HTTP_BAD_REQUEST);
+  }
+
+  /**
+   * Handles a 401 error for JSON.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on401(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_UNAUTHORIZED);
   }
 
   /**
@@ -45,8 +56,7 @@ public function on400(GetResponseForExceptionEvent $event) {
    *   The event to process.
    */
   public function on403(GetResponseForExceptionEvent $event) {
-    $response = new JsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_FORBIDDEN);
-    $event->setResponse($response);
+    $this->setEventResponse($event, Response::HTTP_FORBIDDEN);
   }
 
   /**
@@ -56,8 +66,7 @@ public function on403(GetResponseForExceptionEvent $event) {
    *   The event to process.
    */
   public function on404(GetResponseForExceptionEvent $event) {
-    $response = new JsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_NOT_FOUND);
-    $event->setResponse($response);
+    $this->setEventResponse($event, Response::HTTP_NOT_FOUND);
   }
 
   /**
@@ -67,8 +76,7 @@ public function on404(GetResponseForExceptionEvent $event) {
    *   The event to process.
    */
   public function on405(GetResponseForExceptionEvent $event) {
-    $response = new JsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_METHOD_NOT_ALLOWED);
-    $event->setResponse($response);
+    $this->setEventResponse($event, Response::HTTP_METHOD_NOT_ALLOWED);
   }
 
   /**
@@ -78,8 +86,7 @@ public function on405(GetResponseForExceptionEvent $event) {
    *   The event to process.
    */
   public function on406(GetResponseForExceptionEvent $event) {
-    $response = new JsonResponse(['message' => $event->getException()->getMessage()], Response::HTTP_NOT_ACCEPTABLE);
-    $event->setResponse($response);
+    $this->setEventResponse($event, Response::HTTP_NOT_ACCEPTABLE);
   }
 
   /**
@@ -89,7 +96,32 @@ public function on406(GetResponseForExceptionEvent $event) {
    *   The event to process.
    */
   public function on415(GetResponseForExceptionEvent $event) {
-    $response = new JsonResponse(['message' => $event->getException()->getMessage()], Response::HTTP_UNSUPPORTED_MEDIA_TYPE);
+    $this->setEventResponse($event, Response::HTTP_UNSUPPORTED_MEDIA_TYPE);
+  }
+
+  /**
+   * Sets the Response for the exception event.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The current exception event.
+   * @param int $status
+   *   The HTTP status code to set for the response.
+   */
+  protected function setEventResponse(GetResponseForExceptionEvent $event, $status) {
+    $exception = $event->getException();
+    $response = new CacheableJsonResponse(['message' => $exception->getMessage()], $status);
+    $response->prepare($event->getRequest());
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+
+    // Persist any special HTTP headers that were set on the exception.
+    if ($exception instanceof HttpExceptionInterface) {
+      $response->headers->add($exception->getHeaders());
+    }
+
     $event->setResponse($response);
   }
 
diff --git a/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php b/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php
index a800842..539baa5 100644
--- a/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php
+++ b/core/lib/Drupal/Core/PathProcessor/PathProcessorFront.php
@@ -2,10 +2,10 @@
 
 namespace Drupal\Core\PathProcessor;
 
+use Drupal\Core\Cache\Exception\CacheableNotFoundHttpException;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Render\BubbleableMetadata;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 
 /**
  * Processes the inbound path by resolving it to the front page if empty.
@@ -40,7 +40,7 @@ public function processInbound($path, Request $request) {
       if (empty($path)) {
         // We have to return a valid path but / won't be routable and config
         // might be broken so stop execution.
-        throw new NotFoundHttpException();
+        throw new CacheableNotFoundHttpException();
       }
     }
     return $path;
diff --git a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
index 5c3d931..a758c6d 100644
--- a/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
+++ b/core/lib/Drupal/Core/Routing/AccessAwareRouter.php
@@ -4,9 +4,9 @@
 
 use Drupal\Core\Access\AccessManagerInterface;
 use Drupal\Core\Access\AccessResultReasonInterface;
+use Drupal\Core\Cache\Exception\CacheableAccessDeniedHttpException;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
 use Symfony\Component\Routing\Matcher\RequestMatcherInterface;
 use Symfony\Component\Routing\RequestContext as SymfonyRequestContext;
 use Symfony\Component\Routing\RequestContextAwareInterface;
@@ -111,7 +111,9 @@ protected function checkAccess(Request $request) {
       $request->attributes->set(AccessAwareRouterInterface::ACCESS_RESULT, $access_result);
     }
     if (!$access_result->isAllowed()) {
-      throw new AccessDeniedHttpException($access_result instanceof AccessResultReasonInterface ? $access_result->getReason() : NULL);
+      $exception = new CacheableAccessDeniedHttpException($access_result instanceof AccessResultReasonInterface ? $access_result->getReason() : NULL);
+      $exception->addCacheableDependency($access_result);
+      throw $exception;
     }
   }
 
diff --git a/core/lib/Drupal/Core/Routing/Enhancer/ParamConversionEnhancer.php b/core/lib/Drupal/Core/Routing/Enhancer/ParamConversionEnhancer.php
index e804f2e..35733b4 100644
--- a/core/lib/Drupal/Core/Routing/Enhancer/ParamConversionEnhancer.php
+++ b/core/lib/Drupal/Core/Routing/Enhancer/ParamConversionEnhancer.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Core\Routing\Enhancer;
 
+use Drupal\Core\Cache\Exception\CacheableNotFoundHttpException;
 use Drupal\Core\ParamConverter\ParamConverterManagerInterface;
 use Drupal\Core\ParamConverter\ParamNotConvertedException;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -9,7 +10,6 @@
 use Symfony\Component\HttpFoundation\ParameterBag;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
-use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\Routing\Route;
 
@@ -77,7 +77,7 @@ protected function copyRawVariables(array $defaults) {
   public function onException(GetResponseForExceptionEvent $event) {
     $exception = $event->getException();
     if ($exception instanceof ParamNotConvertedException) {
-      $event->setException(new NotFoundHttpException($exception->getMessage(), $exception));
+      $event->setException(new CacheableNotFoundHttpException($exception->getMessage(), $exception));
     }
   }
 
diff --git a/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php b/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php
index 8119efa..e246c35 100644
--- a/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php
+++ b/core/lib/Drupal/Core/Routing/RequestFormatRouteFilter.php
@@ -2,8 +2,8 @@
 
 namespace Drupal\Core\Routing;
 
+use Drupal\Core\Cache\Exception\CacheableNotAcceptableHttpException;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\NotAcceptableHttpException;
 use Symfony\Component\Routing\Route;
 use Symfony\Component\Routing\RouteCollection;
 
@@ -45,7 +45,7 @@ public function filter(RouteCollection $collection, Request $request) {
     // We do not throw a
     // \Symfony\Component\Routing\Exception\ResourceNotFoundException here
     // because we don't want to return a 404 status code, but rather a 406.
-    throw new NotAcceptableHttpException("No route found for the specified format $format.");
+    throw new CacheableNotAcceptableHttpException("No route found for the specified format $format.");
   }
 
 }
diff --git a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
index eac482c..0e68efa 100644
--- a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
+++ b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
@@ -5,12 +5,12 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Authentication\AuthenticationProviderInterface;
 use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
+use Drupal\Core\Cache\Exception\CacheableUnauthorizedHttpException;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Flood\FloodInterface;
 use Drupal\user\UserAuthInterface;
 use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
 
 /**
  * HTTP Basic authentication provider.
@@ -130,7 +130,7 @@ public function challengeException(Request $request, \Exception $previous) {
     $challenge = SafeMarkup::format('Basic realm="@realm"', array(
       '@realm' => !empty($site_name) ? $site_name : 'Access restricted',
     ));
-    return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
+    return new CacheableUnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
   }
 
 }
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 55ce64a..8ddbda3 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -3,6 +3,7 @@
 namespace Drupal\rest\Plugin\rest\resource;
 
 use Drupal\Component\Plugin\DependentPluginInterface;
+use Drupal\Core\Cache\Exception\CacheableAccessDeniedHttpException;
 use Drupal\Core\Config\Entity\ConfigEntityType;
 use Drupal\Core\Entity\EntityTypeManagerInterface;
 use Drupal\Core\Entity\FieldableEntityInterface;
@@ -106,7 +107,10 @@ public static function create(ContainerInterface $container, array $configuratio
   public function get(EntityInterface $entity) {
     $entity_access = $entity->access('view', NULL, TRUE);
     if (!$entity_access->isAllowed()) {
-      throw new AccessDeniedHttpException();
+      $exception = new CacheableAccessDeniedHttpException();
+      $exception->addCacheableDependency($entity);
+      $exception->addCacheableDependency($entity_access);
+      throw $exception;
     }
 
     $response = new ResourceResponse($entity, 200);
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index 4700b2d..e7c733b 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -88,6 +88,9 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
     $unserialized = NULL;
     if (!empty($received)) {
       $format = $request->getContentType();
+      // Set the request format so an Exception is returned in the proper
+      // format.
+      $request->setRequestFormat($format);
 
       // Only allow serialization formats that are explicitly configured. If no
       // formats are configured allow all and hope that the serializer knows the
diff --git a/core/modules/rest/src/Tests/AuthTest.php b/core/modules/rest/src/Tests/AuthTest.php
index 9f4224f..1e570ac 100644
--- a/core/modules/rest/src/Tests/AuthTest.php
+++ b/core/modules/rest/src/Tests/AuthTest.php
@@ -34,7 +34,7 @@ public function testRead() {
     // Try to read the resource as an anonymous user, which should not work.
     $this->httpRequest($entity->urlInfo()->setRouteParameter('_format', $this->defaultFormat), 'GET');
     $this->assertResponse('401', 'HTTP response code is 401 when the request is not authenticated and the user is anonymous.');
-    $this->assertRaw(json_encode(['message' => 'A fatal error occurred: No authentication credentials provided.']));
+    $this->assertRaw(json_encode(['message' => 'No authentication credentials provided.']));
 
     // Ensure that cURL settings/headers aren't carried over to next request.
     unset($this->curlHandle);
diff --git a/core/modules/rest/src/Tests/NodeTest.php b/core/modules/rest/src/Tests/NodeTest.php
index 95dc475..ca73e70 100644
--- a/core/modules/rest/src/Tests/NodeTest.php
+++ b/core/modules/rest/src/Tests/NodeTest.php
@@ -174,7 +174,7 @@ public function testInvalidBundle() {
 
     // Make sure the response is "Bad Request".
     $this->assertResponse(400);
-    $this->assertResponseBody('{"error":"\"bad_bundle_name\" is not a valid bundle type for denormalization."}');
+    $this->assertResponseBody('{"message":"\\u0022bad_bundle_name\\u0022 is not a valid bundle type for denormalization."}');
   }
 
   /**
@@ -191,7 +191,7 @@ public function testMissingBundle() {
 
     // Make sure the response is "Bad Request".
     $this->assertResponse(400);
-    $this->assertResponseBody('{"error":"A string must be provided as a bundle value."}');
+    $this->assertResponseBody('{"message":"A string must be provided as a bundle value."}');
   }
 
 }
diff --git a/core/modules/rest/src/Tests/UpdateTest.php b/core/modules/rest/src/Tests/UpdateTest.php
index 287c2df..97970a3 100644
--- a/core/modules/rest/src/Tests/UpdateTest.php
+++ b/core/modules/rest/src/Tests/UpdateTest.php
@@ -370,7 +370,7 @@ protected function patchEntity(EntityInterface $entity, array $read_only_fields,
 
       $this->httpRequest($url, 'PATCH', $serialized, $mime_type);
       $this->assertResponse(403);
-      $this->assertResponseBody('{"message":"Access denied on updating field \\u0027' . $field . '\\u0027."}');
+      $this->assertResponseBody('{"message":"Access denied on updating field \'' . $field . '\'."}');
 
       if ($format === 'hal_json') {
         // We've just tried with this read-only field, now unset it.
diff --git a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
index ba91836..39eedc3 100644
--- a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
+++ b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
@@ -2,9 +2,12 @@
 
 namespace Drupal\serialization\EventSubscriber;
 
+use Drupal\Core\Cache\CacheableResponse;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
 use Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 use Symfony\Component\Serializer\SerializerInterface;
 
 /**
@@ -66,6 +69,16 @@ public function on400(GetResponseForExceptionEvent $event) {
   }
 
   /**
+   * Handles a 401 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on401(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_UNAUTHORIZED);
+  }
+
+  /**
    * Handles a 403 error for HTTP.
    *
    * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
@@ -134,10 +147,24 @@ public function on429(GetResponseForExceptionEvent $event) {
    *   The HTTP status code to set for the response.
    */
   protected function setEventResponse(GetResponseForExceptionEvent $event, $status) {
-    $format = $event->getRequest()->getRequestFormat();
-    $content = ['message' => $event->getException()->getMessage()];
+    $exception = $event->getException();
+    $request = $event->getRequest();
+    $format = $request->getRequestFormat();
+    $content = ['message' => $exception->getMessage()];
     $encoded_content = $this->serializer->serialize($content, $format);
-    $response = new Response($encoded_content, $status);
+    $response = new CacheableResponse($encoded_content, $status);
+    $response->prepare($request);
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+
+    // Persist any special HTTP headers that were set on the exception.
+    if ($exception instanceof HttpExceptionInterface) {
+      $response->headers->add($exception->getHeaders());
+    }
+
     $event->setResponse($response);
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
index f150e4fe..407e75d 100644
--- a/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
+++ b/core/tests/Drupal/Tests/Core/Entity/EntityUnitTest.php
@@ -392,11 +392,13 @@ public function testPostSave() {
     $this->cacheTagsInvalidator->expects($this->at(0))
       ->method('invalidateTags')
       ->with(array(
+        '4xx-response',
         $this->entityTypeId . '_list', // List cache tag.
       ));
     $this->cacheTagsInvalidator->expects($this->at(1))
       ->method('invalidateTags')
       ->with(array(
+        '4xx-response',
         $this->entityTypeId . ':' . $this->values['id'], // Own cache tag.
         $this->entityTypeId . '_list', // List cache tag.
       ));
