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/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..35c0f88 100644
--- a/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/DefaultExceptionSubscriber.php
@@ -3,6 +3,9 @@
 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;
@@ -119,7 +122,15 @@ 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);
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response = new CacheableResponse($content, 500);
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+    else {
+      $response = new Response($content, 500);
+    }
 
     if ($exception instanceof HttpExceptionInterface) {
       $response->setStatusCode($exception->getStatusCode());
@@ -151,7 +162,15 @@ protected function onJson(GetResponseForExceptionEvent $event) {
       $data = ['message' => sprintf('A fatal error occurred: %s', $message)];
     }
 
-    $response = new JsonResponse($data, Response::HTTP_INTERNAL_SERVER_ERROR);
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response = new CacheableJsonResponse($data, Response::HTTP_INTERNAL_SERVER_ERROR);
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+    else {
+      $response = new JsonResponse($data, Response::HTTP_INTERNAL_SERVER_ERROR);
+    }
+
     if ($exception instanceof HttpExceptionInterface) {
       $response->setStatusCode($exception->getStatusCode());
       $response->headers->add($exception->getHeaders());
@@ -170,7 +189,15 @@ 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());
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response = new CacheableResponse($exception->getMessage(), $exception->getStatusCode(), $exception->getHeaders());
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+    else {
+      $response = new Response($exception->getMessage(), $exception->getStatusCode(), $exception->getHeaders());
+    }
+
     $event->setResponse($response);
   }
 
diff --git a/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
index 2853b98..c3c0047 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
@@ -2,9 +2,12 @@
 
 namespace Drupal\Core\EventSubscriber;
 
+use Drupal\Core\Cache\CacheableJsonResponse;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
 use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
+use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
 
 /**
  * Default handling for JSON errors.
@@ -34,8 +37,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 +57,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 +67,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 +77,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 +87,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 +97,34 @@ 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();
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response = new CacheableJsonResponse(['message' => $exception->getMessage()], $status);
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+    else {
+      $response = new JsonResponse(['message' => $exception->getMessage()], $status);
+    }
+
+    // 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/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/modules/basic_auth/src/Authentication/Provider/BasicAuth.php b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
index eac482c..b5cd302 100644
--- a/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
+++ b/core/modules/basic_auth/src/Authentication/Provider/BasicAuth.php
@@ -5,6 +5,8 @@
 use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Authentication\AuthenticationProviderInterface;
 use Drupal\Core\Authentication\AuthenticationProviderChallengeInterface;
+use Drupal\Core\Cache\Exception\CacheableHttpExceptionInterface;
+use Drupal\Core\Cache\Exception\CacheableUnauthorizedHttpException;
 use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Flood\FloodInterface;
@@ -130,6 +132,15 @@ public function challengeException(Request $request, \Exception $previous) {
     $challenge = SafeMarkup::format('Basic realm="@realm"', array(
       '@realm' => !empty($site_name) ? $site_name : 'Access restricted',
     ));
+
+
+    // Persist any cacheability metadata.
+    if ($previous instanceof CacheableHttpExceptionInterface) {
+      $exception = new CacheableUnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
+      $exception->addCacheableDependency($previous->getCacheableMetadata());
+      return $exception;
+    }
+
     return new UnauthorizedHttpException((string) $challenge, 'No authentication credentials provided.', $previous);
   }
 
diff --git a/core/modules/hal/tests/src/Functional/HalJsonBasicAuthWorkaroundFor2805281Trait.php b/core/modules/hal/tests/src/Functional/HalJsonBasicAuthWorkaroundFor2805281Trait.php
index ef77d96..10ec699 100644
--- a/core/modules/hal/tests/src/Functional/HalJsonBasicAuthWorkaroundFor2805281Trait.php
+++ b/core/modules/hal/tests/src/Functional/HalJsonBasicAuthWorkaroundFor2805281Trait.php
@@ -20,7 +20,7 @@ protected function assertResponseWhenMissingAuthentication(ResponseInterface $re
     // @todo this works fine locally, but on testbot it comes back with
     // 'text/plain; charset=UTF-8'. WTF.
     // $this->assertSame(['application/hal+json'], $response->getHeader('Content-Type'));
-    $this->assertSame('No authentication credentials provided.', (string) $response->getBody());
+    $this->assertSame(json_encode(['message' => 'No authentication credentials provided.']), (string) $response->getBody());
   }
 
 }
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/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
index ba91836..8223920 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
@@ -106,6 +119,16 @@ public function on406(GetResponseForExceptionEvent $event) {
   }
 
   /**
+   * Handles a 415 error for HTTP.
+   *
+   * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
+   *   The event to process.
+   */
+  public function on415(GetResponseForExceptionEvent $event) {
+    $this->setEventResponse($event, Response::HTTP_UNSUPPORTED_MEDIA_TYPE);
+  }
+
+  /**
    * Handles a 422 error for HTTP.
    *
    * @param \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent $event
@@ -134,10 +157,25 @@ public function on429(GetResponseForExceptionEvent $event) {
    *   The HTTP status code to set for the response.
    */
   protected function setEventResponse(GetResponseForExceptionEvent $event, $status) {
+    $exception = $event->getException();
     $format = $event->getRequest()->getRequestFormat();
-    $content = ['message' => $event->getException()->getMessage()];
+    $content = ['message' => $exception->getMessage()];
     $encoded_content = $this->serializer->serialize($content, $format);
-    $response = new Response($encoded_content, $status);
+
+    // Persist any cacheability metadata.
+    if ($exception instanceof CacheableHttpExceptionInterface) {
+      $response = new CacheableResponse($encoded_content, $status);
+      $response->addCacheableDependency($exception->getCacheableMetadata());
+    }
+    else {
+      $response = new Response($encoded_content, $status);
+    }
+
+    // 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.
       ));
