diff --git a/core/modules/jsonapi/src/Normalizer/HttpExceptionNormalizer.php b/core/modules/jsonapi/src/Normalizer/HttpExceptionNormalizer.php
index 5062f11a696efe052983514f800e99f443c1bcdc..7737ce00cf82e49bee2a76f4f5199c853b7c2ab1 100644
--- a/core/modules/jsonapi/src/Normalizer/HttpExceptionNormalizer.php
+++ b/core/modules/jsonapi/src/Normalizer/HttpExceptionNormalizer.php
@@ -49,7 +49,9 @@ public function __construct(AccountInterface $current_user) {
    * {@inheritdoc}
    */
   public function normalize($object, $format = NULL, array $context = []) {
-    return new HttpExceptionNormalizerValue(new CacheableMetadata(), static::rasterizeValueRecursive($this->buildErrorObjects($object)));
+    $cacheability = new CacheableMetadata();
+    $cacheability->addCacheableDependency($object);
+    return new HttpExceptionNormalizerValue($cacheability, static::rasterizeValueRecursive($this->buildErrorObjects($object)));
   }
 
   /**
diff --git a/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php b/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php
index 9961720018a0b5072d74ba673c69570a7e7183cd..43fffa436fcfa244577706a38e9cf696a5c0de65 100644
--- a/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php
+++ b/core/modules/jsonapi/tests/src/Functional/JsonApiRegressionTest.php
@@ -1131,4 +1131,60 @@ public function testEmptyMapFieldTypeDenormalization() {
     $this->assertSame($doc['data']['attributes']['data'], Json::decode((string) $response->getBody())['data']['attributes']['data']);
   }
 
+  /**
+   * Ensure EntityAccessDeniedHttpException cacheability is taken into account.
+   */
+  public function testLeakCacheMetadataInOmitted() {
+    $term = Term::create([
+      'name' => 'Llama term',
+      'vid' => 'tags',
+    ]);
+    $term->setUnpublished();
+    $term->save();
+
+    $node = Node::create([
+      'type' => 'article',
+      'title' => 'Llama node',
+      'field_tags' => ['target_id' => $term->id()],
+    ]);
+    $node->save();
+
+    $user = $this->drupalCreateUser([
+      'access content',
+    ]);
+    $request_options = [
+      RequestOptions::AUTH => [
+        $user->getAccountName(),
+        $user->pass_raw,
+      ],
+    ];
+
+    // Request with unpublished term. At this point it would include the term
+    // into "omitted" part of the response. The point here is that we
+    // purposefully warm up the cache where it is excluded from response and
+    // on the next run we will assure merely publishing term is enough to make
+    // it visible, i.e. that the 1st response was invalidated in Drupal cache.
+    $url = Url::fromUri('internal:/jsonapi/' . $node->getEntityTypeId() . '/' . $node->bundle(), [
+      'query' => ['include' => 'field_tags'],
+    ]);
+    $response = $this->request('GET', $url, $request_options);
+    $this->assertSame(200, $response->getStatusCode());
+
+    $response = Json::decode((string) $response->getBody());
+    $this->assertArrayNotHasKey('included', $response, 'JSON API response does not contain "included" taxonomy term as the latter is not published, i.e not accessible.');
+
+    $omitted = $response['meta']['omitted']['links'];
+    unset($omitted['help']);
+    $omitted = reset($omitted);
+    $expected_url = Url::fromUri('internal:/jsonapi/' . $term->getEntityTypeId() . '/' . $term->bundle() . '/' . $term->uuid());
+    $expected_url->setAbsolute();
+    $this->assertSame($expected_url->toString(), $omitted['href'], 'Entity that is excluded due to access constraints is correctly reported in the "Omitted" section of the JSON API response.');
+
+    $term->setPublished();
+    $term->save();
+    $response = $this->request('GET', $url, $request_options);
+    $this->assertSame(200, $response->getStatusCode());
+    $this->assertEquals($term->uuid(), Json::decode((string) $response->getBody())['included'][0]['id'], 'JSON API response contains "included" taxonomy term as it became published, i.e accessible.');
+  }
+
 }