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/ExceptionJsonSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
index 2853b98..5b73515 100644
--- a/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/ExceptionJsonSubscriber.php
@@ -2,7 +2,7 @@
 
 namespace Drupal\Core\EventSubscriber;
 
-use Symfony\Component\HttpFoundation\JsonResponse;
+use Drupal\Core\Cache\CacheableJsonResponse;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
 
@@ -45,7 +45,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);
+    $response = new CacheableJsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_FORBIDDEN);
     $event->setResponse($response);
   }
 
@@ -56,7 +56,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);
+    $response = new CacheableJsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_NOT_FOUND);
     $event->setResponse($response);
   }
 
@@ -67,7 +67,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);
+    $response = new CacheableJsonResponse(array('message' => $event->getException()->getMessage()), Response::HTTP_METHOD_NOT_ALLOWED);
     $event->setResponse($response);
   }
 
@@ -78,7 +78,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);
+    $response = new CacheableJsonResponse(['message' => $event->getException()->getMessage()], Response::HTTP_NOT_ACCEPTABLE);
     $event->setResponse($response);
   }
 
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index 5cf42dd..cbb2b5d 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -103,7 +103,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();
+      $response = new ResourceResponse(NULL, 403);
+      $response->addCacheableDependency($entity);
+      $response->addCacheableDependency($entity_access);
+      return $response;
     }
 
     $response = new ResourceResponse($entity, 200);
@@ -141,8 +144,11 @@ public function post(EntityInterface $entity = NULL) {
       throw new BadRequestHttpException('No entity content received.');
     }
 
-    if (!$entity->access('create')) {
-      throw new AccessDeniedHttpException();
+    $entity_access = $entity->access('create', NULL, TRUE);
+    if (!$entity_access->isAllowed()) {
+      $response = new ResourceResponse(NULL, 403);
+      $response->addCacheableDependency($entity_access);
+      return $response;
     }
     $definition = $this->getPluginDefinition();
     // Verify that the deserialized entity is of the type that we expect to
@@ -160,8 +166,14 @@ public function post(EntityInterface $entity = NULL) {
     // submitted by the user. Field access makes no difference between 'create'
     // and 'update', so the 'edit' operation is used here.
     foreach ($entity->_restSubmittedFields as $key => $field_name) {
-      if (!$entity->get($field_name)->access('edit')) {
-        throw new AccessDeniedHttpException("Access denied on creating field '$field_name'");
+      $field_access = $entity->get($field_name)->access('edit', NULL, TRUE);
+      if (!$field_access->isAllowed()) {
+        $response = new ResourceResponse([
+          'error' => "Access denied on creating field '$field_name'"
+        ], 403);
+        $response->addCacheableDependency($entity_access);
+        $response->addCacheableDependency($field_access);
+        return $response;
       }
     }
 
@@ -204,8 +216,12 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
     if ($entity->getEntityTypeId() != $definition['entity_type']) {
       throw new BadRequestHttpException('Invalid entity type');
     }
-    if (!$original_entity->access('update')) {
-      throw new AccessDeniedHttpException();
+    $original_entity_access = $original_entity->access('update', NULL, TRUE);
+    if (!$original_entity_access->isAllowed()) {
+      $response = new ResourceResponse(NULL, 403);
+      $response->addCacheableDependency($original_entity);
+      $response->addCacheableDependency($original_entity_access);
+      return $response;
     }
 
     // Overwrite the received properties.
@@ -229,8 +245,15 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
         }
       }
 
-      if (!$original_entity->get($field_name)->access('edit')) {
-        throw new AccessDeniedHttpException("Access denied on updating field '$field_name'.");
+      $field_access = $original_entity->get($field_name)->access('edit', NULL, TRUE);
+      if (!$field_access->isAllowed()) {
+        $response = new ResourceResponse([
+          'error' => "Access denied on updating field '$field_name'."
+        ], 403);
+        $response->addCacheableDependency($original_entity);
+        $response->addCacheableDependency($original_entity_access);
+        $response->addCacheableDependency($field_access);
+        return $response;
       }
       $original_entity->set($field_name, $field->getValue());
     }
@@ -261,8 +284,12 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
    * @throws \Symfony\Component\HttpKernel\Exception\HttpException
    */
   public function delete(EntityInterface $entity) {
-    if (!$entity->access('delete')) {
-      throw new AccessDeniedHttpException();
+    $entity_access = $entity->access('delete', NULL, TRUE);
+    if (!$entity_access->isAllowed()) {
+      $response = new ResourceResponse(NULL, 403);
+      $response->addCacheableDependency($entity);
+      $response->addCacheableDependency($entity_access);
+      return $response;
     }
     try {
       $entity->delete();
diff --git a/core/modules/rest/src/RequestHandler.php b/core/modules/rest/src/RequestHandler.php
index 088dca2..dfe8a01 100644
--- a/core/modules/rest/src/RequestHandler.php
+++ b/core/modules/rest/src/RequestHandler.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\rest;
 
+use Symfony;
+use Drupal\Core\Cache\CacheableResponse;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Cache\CacheableResponseInterface;
@@ -12,6 +14,8 @@
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;
+use Symfony\Component\HttpKernel\Exception\HttpException;
 use Symfony\Component\HttpKernel\Exception\UnsupportedMediaTypeHttpException;
 use Symfony\Component\Serializer\Exception\UnexpectedValueException;
 use Symfony\Component\Serializer\SerializerInterface;
@@ -107,9 +111,7 @@ public function handle(RouteMatchInterface $route_match, Request $request) {
           }
         }
         catch (UnexpectedValueException $e) {
-          $error['error'] = $e->getMessage();
-          $content = $serializer->serialize($error, $format);
-          return new Response($content, 400, array('Content-Type' => $request->getMimeType($format)));
+          throw new BadRequestHttpException($e->getMessage(), $e);
         }
       }
       else {
diff --git a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
index ba91836..894776d 100644
--- a/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
+++ b/core/modules/serialization/src/EventSubscriber/DefaultExceptionSubscriber.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\serialization\EventSubscriber;
 
+use Drupal\Core\Cache\CacheableResponse;
 use Drupal\Core\EventSubscriber\HttpExceptionSubscriberBase;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent;
@@ -137,7 +138,7 @@ protected function setEventResponse(GetResponseForExceptionEvent $event, $status
     $format = $event->getRequest()->getRequestFormat();
     $content = ['message' => $event->getException()->getMessage()];
     $encoded_content = $this->serializer->serialize($content, $format);
-    $response = new Response($encoded_content, $status);
+    $response = new CacheableResponse($encoded_content, $status);
     $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.
       ));
