diff --git a/core/modules/node/src/NodeAccessControlHandler.php b/core/modules/node/src/NodeAccessControlHandler.php
index 963ab53ded4129547c9480f9d29937de2ff7b21a..11561df302fe734fc3e18ab7f3818d6ebc3fce0c 100644
--- a/core/modules/node/src/NodeAccessControlHandler.php
+++ b/core/modules/node/src/NodeAccessControlHandler.php
@@ -131,12 +131,11 @@ protected function checkAccess(EntityInterface $node, $operation, AccountInterfa
     assert($node instanceof NodeInterface);
     $cacheability = new CacheableMetadata();
 
+    $view_access_result = NULL;
+
     /** @var \Drupal\node\NodeInterface $node */
     if ($operation === 'view') {
-      $result = $this->checkViewAccess($node, $account, $cacheability);
-      if ($result !== NULL) {
-        return $result;
-      }
+      $view_access_result = $this->checkViewAccess($node, $account, $cacheability);
     }
 
     [$revision_permission_operation, $entity_operation] = static::REVISION_OPERATION_MAP[$operation] ?? [
@@ -185,6 +184,9 @@ protected function checkAccess(EntityInterface $node, $operation, AccountInterfa
     $access_result = $this->grantStorage->access($node, $operation, $account);
     if ($access_result instanceof RefinableCacheableDependencyInterface) {
       $access_result->addCacheableDependency($cacheability);
+      if ($view_access_result) {
+        $access_result->addCacheableDependency($view_access_result);
+      }
     }
     return $access_result;
   }
@@ -216,14 +218,31 @@ protected function checkViewAccess(NodeInterface $node, AccountInterface $accoun
       return NULL;
     }
 
+    // Due to the check below, it is not possible to rely only on account
+    // permissions to determine whether the 'view own unpublished content'
+    // permission can be checked, instead we also need to check if the user has
+    // the authenticated role. Just in case anonymous and authenticated users
+    // are both granted the 'view own unpublished content' permission and also
+    // have otherwise identical permissions.
     $cacheability->addCacheContexts(['user.roles:authenticated']);
+
     // The "view own unpublished content" permission must not be granted
     // to anonymous users for security reasons.
     if (!$account->isAuthenticated()) {
       return NULL;
     }
 
+    // When access is granted due to the 'view own unpublished content'
+    // permission and for no other reason, node grants are bypassed. However,
+    // to ensure the full set of cacheable metadata is available to variation
+    // cache, additionally add the node_grants cache context so that if the
+    // status or the owner of the node changes, cache redirects will continue to
+    // reflect the latest state without needing to be invalidated.
     $cacheability->addCacheContexts(['user']);
+    if ($this->moduleHandler->hasImplementations('node_grants')) {
+      $cacheability->addCacheContexts(['user.node_grants:view']);
+    }
+
     if ($account->id() != $node->getOwnerId()) {
       return NULL;
     }
diff --git a/core/modules/node/tests/src/Functional/NodeAccessCacheRedirectWarningTest.php b/core/modules/node/tests/src/Functional/NodeAccessCacheRedirectWarningTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..8946ca75fefeee4734ce41eafc516ac136b8a5d7
--- /dev/null
+++ b/core/modules/node/tests/src/Functional/NodeAccessCacheRedirectWarningTest.php
@@ -0,0 +1,72 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\Tests\node\Functional;
+
+/**
+ * Tests the node access grants cache context service.
+ *
+ * @group node
+ * @group Cache
+ */
+class NodeAccessCacheRedirectWarningTest extends NodeTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected static $modules = ['block', 'node_access_test_empty'];
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp(): void {
+    parent::setUp();
+
+    node_access_rebuild();
+  }
+
+  /**
+   * Quick demonstration of the differences in cache contexts.
+   *
+   * The intent here was to visit the nodes to view the error but for whatever
+   * reason I can't seem to trigger the redirect warning this way. Needs work.
+   */
+  public function testNodeAccessCacheRedirectWarning(): void {
+    $this->drupalPlaceBlock('local_tasks_block');
+
+    $this->assertTrue(\Drupal::moduleHandler()->hasImplementations('node_grants'));
+
+    $author = $this->drupalCreateUser([
+      'create page content',
+      'edit any page content',
+      'view own unpublished content',
+    ]);
+    $this->drupalLogin($author);
+
+    $node = $this->drupalCreateNode(['uid' => $author->id(), 'status' => 0]);
+
+    $this->drupalGet($node->toUrl());
+
+    $node->setPublished();
+    $node->save();
+
+    $this->drupalGet($node->toUrl());
+
+    $node->setUnpublished();
+    $node->save();
+
+    $this->drupalGet($node->toUrl());
+
+    $node->setPublished();
+    $node->save();
+
+    $this->drupalGet($node->toUrl());
+  }
+
+}