diff --git a/core/lib/Drupal/Core/Entity/EntityAccessCheck.php b/core/lib/Drupal/Core/Entity/EntityAccessCheck.php
index bc8bd59..0dc91b5 100644
--- a/core/lib/Drupal/Core/Entity/EntityAccessCheck.php
+++ b/core/lib/Drupal/Core/Entity/EntityAccessCheck.php
@@ -3,6 +3,7 @@
 namespace Drupal\Core\Entity;
 
 use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Access\AccessResultForbidden;
 use Drupal\Core\Routing\Access\AccessInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Drupal\Core\Session\AccountInterface;
@@ -58,7 +59,12 @@ public function access(Route $route, RouteMatchInterface $route_match, AccountIn
     if ($parameters->has($entity_type)) {
       $entity = $parameters->get($entity_type);
       if ($entity instanceof EntityInterface) {
-        return $entity->access($operation, $account, TRUE);
+        $result = $entity->access($operation, $account, TRUE);
+        if ($result->isForbidden()) {
+          return AccessResultForbidden::forbidden("You are not authorized to view this {$entity->getEntityTypeId()} entity of bundle {$entity->bundle()}.")
+            ->mergeCacheMaxAge($result);
+        }
+        return $result;
       }
     }
     // No opinion, so other access checks should decide if access should be
diff --git a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
index 62f5486..be3f55e 100644
--- a/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/AuthenticationSubscriber.php
@@ -96,7 +96,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 AccessDeniedHttpException('The used authentication method is not allowed on this route.');
       }
     }
   }
diff --git a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
index a5cb361..5e279a6 100644
--- a/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
+++ b/core/modules/rest/src/Plugin/rest/resource/EntityResource.php
@@ -106,7 +106,7 @@ 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();
+      throw new AccessDeniedHttpException("You are not authorized to view this {$entity->getEntityTypeId()} entity of bundle {$entity->bundle()}.");
     }
 
     $response = new ResourceResponse($entity, 200);
@@ -145,7 +145,7 @@ public function post(EntityInterface $entity = NULL) {
     }
 
     if (!$entity->access('create')) {
-      throw new AccessDeniedHttpException();
+      throw new AccessDeniedHttpException("You are not authorized to create this {$entity->getEntityTypeId()} entity of bundle {$entity->bundle()}.");
     }
     $definition = $this->getPluginDefinition();
     // Verify that the deserialized entity is of the type that we expect to
@@ -200,7 +200,7 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
       throw new BadRequestHttpException('Invalid entity type');
     }
     if (!$original_entity->access('update')) {
-      throw new AccessDeniedHttpException();
+      throw new AccessDeniedHttpException("You are not authorized to update this {$entity->getEntityTypeId()} entity of bundle {$entity->bundle()}.");
     }
 
     // Overwrite the received properties.
@@ -264,7 +264,7 @@ public function patch(EntityInterface $original_entity, EntityInterface $entity
    */
   public function delete(EntityInterface $entity) {
     if (!$entity->access('delete')) {
-      throw new AccessDeniedHttpException();
+      throw new AccessDeniedHttpException("You are not authorized to delete this {$entity->getEntityTypeId()} entity of bundle {$entity->bundle()}.");
     }
     try {
       $entity->delete();
diff --git a/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php b/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php
index 18dc296..f0c6fd5 100644
--- a/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php
+++ b/core/modules/rest/tests/src/Functional/CookieResourceTestTrait.php
@@ -92,7 +92,7 @@ protected function getAuthenticationRequestOptions($method) {
    * {@inheritdoc}
    */
   protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
-    $this->assertResourceErrorResponse(403, '', $response);
+    $this->assertResourceErrorResponse(403, FALSE, $response);
   }
 
   /**
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
index 48ed438..992f60e 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/EntityResourceTestBase.php
@@ -301,7 +301,7 @@ public function testGet() {
     // response because ?_format query string is present.
     $response = $this->request('GET', $url, $request_options);
     if ($has_canonical_url) {
-      $this->assertResourceErrorResponse(403, '', $response);
+      $this->assertResourceErrorResponse(403, "You are not authorized to view this {$this->entity->getEntityTypeId()} entity of bundle {$this->entity->bundle()}.", $response);
     }
     else {
       $this->assertResourceErrorResponse(404, 'No route found for "GET ' . str_replace($this->baseUrl, '', $this->getUrl()->setAbsolute()->toString()) . '"', $response);
@@ -340,7 +340,8 @@ public function testGet() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('GET', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
+    // In case we have a BC layer, permissions are used, which don't have a good
+    // error message.
     $this->assertResourceErrorResponse(403, '', $response);
 
 
@@ -399,7 +400,8 @@ public function testGet() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('GET', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
+    // In case we have a BC layer, permissions are used, which don't have a good
+    // error message.
     $this->assertResourceErrorResponse(403, '', $response);
 
 
@@ -554,8 +556,7 @@ public function testPost() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('POST', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
-    $this->assertResourceErrorResponse(403, '', $response);
+    $this->assertResourceErrorResponse(403, "You are not authorized to create this {$this->entity->getEntityTypeId()} entity of bundle {$this->entity->bundle()}.", $response);
 
 
     $this->setUpAuthorization('POST');
@@ -622,7 +623,8 @@ public function testPost() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('POST', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
+    // In case we have a BC layer, permissions are used, which don't have a good
+    // error message.
     $this->assertResourceErrorResponse(403, '', $response);
 
 
@@ -746,8 +748,7 @@ public function testPatch() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('PATCH', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
-    $this->assertResourceErrorResponse(403, '', $response);
+    $this->assertResourceErrorResponse(403, "You are not authorized to update this {$this->entity->getEntityTypeId()} entity of bundle {$this->entity->bundle()}.", $response);
 
 
     $this->setUpAuthorization('PATCH');
@@ -829,7 +830,8 @@ public function testPatch() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('PATCH', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
+    // In case we have a BC layer, permissions are used, which don't have a good
+    // error message.
     $this->assertResourceErrorResponse(403, '', $response);
 
 
@@ -899,8 +901,7 @@ public function testDelete() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('DELETE', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
-    $this->assertResourceErrorResponse(403, '', $response);
+    $this->assertResourceErrorResponse(403, "You are not authorized to delete this {$this->entity->getEntityTypeId()} entity of bundle {$this->entity->bundle()}.", $response);
 
 
     $this->setUpAuthorization('DELETE');
@@ -929,7 +930,8 @@ public function testDelete() {
 
     // DX: 403 when unauthorized.
     $response = $this->request('DELETE', $url, $request_options);
-    // @todo Update the message in https://www.drupal.org/node/2808233.
+    // In case we have a BC layer, permissions are used, which don't have a good
+    // error message.
     $this->assertResourceErrorResponse(403, '', $response);
 
 
diff --git a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php b/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php
index 75fcd08..f956bfa 100644
--- a/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php
+++ b/core/modules/rest/tests/src/Functional/EntityResource/Role/RoleJsonBasicAuthTest.php
@@ -3,6 +3,7 @@
 namespace Drupal\Tests\rest\Functional\EntityResource\Role;
 
 use Drupal\Tests\rest\Functional\BasicAuthResourceTestTrait;
+use Drupal\Tests\rest\Functional\JsonBasicAuthWorkaroundFor2805281Trait;
 use Psr\Http\Message\ResponseInterface;
 
 /**
@@ -37,12 +38,9 @@ class RoleJsonBasicAuthTest extends RoleResourceTestBase {
    */
   protected static $auth = 'basic_auth';
 
-  /**
-   * {@inheritdoc}
-   */
-  protected function assertResponseWhenMissingAuthentication(ResponseInterface $response) {
-    $this->assertSame(401, $response->getStatusCode());
-    $this->assertSame('{"message":"A fatal error occurred: No authentication credentials provided."}', (string) $response->getBody());
+  // @todo Fix in https://www.drupal.org/node/2805281: remove this trait usage.
+  use JsonBasicAuthWorkaroundFor2805281Trait {
+    JsonBasicAuthWorkaroundFor2805281Trait::assertResponseWhenMissingAuthentication insteadof BasicAuthResourceTestTrait;
   }
 
 }
diff --git a/core/modules/rest/tests/src/Functional/ResourceTestBase.php b/core/modules/rest/tests/src/Functional/ResourceTestBase.php
index 49ef8e5..7fe1c91 100644
--- a/core/modules/rest/tests/src/Functional/ResourceTestBase.php
+++ b/core/modules/rest/tests/src/Functional/ResourceTestBase.php
@@ -340,7 +340,7 @@ protected function assertResourceResponse($expected_status_code, $expected_body,
    *   The error response to assert.
    */
   protected function assertResourceErrorResponse($expected_status_code, $expected_message, ResponseInterface $response) {
-    $expected_body = $this->serializer->encode(['message' => $expected_message], static::$format);
+    $expected_body = ($expected_message !== FALSE) ? $this->serializer->encode(['message' => $expected_message], static::$format) : FALSE;
     $this->assertResourceResponse($expected_status_code, $expected_body, $response);
   }
 
