diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 7c371d6..3a14dd6 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1064,6 +1064,21 @@ function node_query_node_access_alter(AlterableInterface $query) {
 
   // Update the query for the given storage method.
   \Drupal::service('node.grant_storage')->alterQuery($query, $tables, $op, $account, $base_table);
+
+  // Code that performs a node_access query should explicitly add the
+  // 'user.node_grants:$op' cache context to the render element or
+  // \Drupal\Core\Cache\CacheableDependencyInterface object that is affected
+  // by the query. Doing so ensures that the context is attached to the most
+  // appropriate element or object. However, since cache contexts are new to
+  // Drupal 8, to safeguard route controllers or other code that forget to do
+  // this, here we also add it to the current render context.
+  // @see \Drupal\Core\Render\MetadataBubblingUrlGenerator::bubble()
+  // @todo Remove before Drupal 9.0.0.
+  $renderer = \Drupal::service('renderer');
+  if ($renderer->hasRenderContext()) {
+    $build = ['#cache' => ['contexts' => ['user.node_grants:' . $op]]];
+    $renderer->render($build);
+  }
 }
 
 /**
diff --git a/core/modules/node/src/Tests/NodeAccessGrantsCacheContextTest.php b/core/modules/node/src/Tests/NodeAccessGrantsCacheContextTest.php
index 1d34bc1..b8fad35 100644
--- a/core/modules/node/src/Tests/NodeAccessGrantsCacheContextTest.php
+++ b/core/modules/node/src/Tests/NodeAccessGrantsCacheContextTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\node\Tests;
 
+use Drupal\Core\Url;
+
 /**
  * Tests the node access grants cache context service.
  *
@@ -140,4 +142,38 @@ public function testCacheContext() {
     ]);
   }
 
+  /**
+   * Tests that the node grants cache context is auto-added, only when needed.
+   *
+   * @see node_query_node_access_alter()
+   */
+  public function testAutomaticNodeAccessGrantsCacheContextBubbling() {
+    $this->dumpHeaders = TRUE;
+
+    // Install the module that contains our test route and controller.
+    $this->container->get('module_installer')->install(['node_access_test_auto_bubbling']);
+    $this->rebuildContainer();
+    $this->container->get('router.builder')->rebuild();
+
+    // The node grants cache context should be added automatically.
+    $this->drupalGet(new Url('node_access_test_auto_bubbling'));
+    $this->assertCacheContext('user.node_grants:view');
+
+    // The root user has the 'bypass node access' permission, which means the
+    // node grants cache context is not necessary.
+    $this->drupalLogin($this->rootUser);
+    $this->drupalGet(new Url('node_access_test_auto_bubbling'));
+    $this->assertNoCacheContext('user.node_grants:view');
+    $this->drupalLogout();
+
+    // Uninstall the module with the only hook_node_grants() implementation.
+    $this->container->get('module_installer')->uninstall(['node_access_test']);
+    $this->rebuildContainer();
+
+    // Because there are no node grants defined, there also is no need for the
+    // node grants cache context to be bubbled.
+    $this->drupalGet(new Url('node_access_test_auto_bubbling'));
+    $this->assertNoCacheContext('user.node_grants:view');
+  }
+
 }
diff --git a/core/modules/node/tests/modules/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml b/core/modules/node/tests/modules/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml
new file mode 100644
index 0000000..49a990d
--- /dev/null
+++ b/core/modules/node/tests/modules/node_access_test_auto_bubbling/node_access_test_auto_bubbling.info.yml
@@ -0,0 +1,6 @@
+name: 'Node module access automatic cacheability bubbling tests'
+type: module
+description: 'Support module for node permission testing. Provides a route which does a node access query without explicitly specifying the corresponding cache context.'
+package: Testing
+version: VERSION
+core: 8.x
diff --git a/core/modules/node/tests/modules/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml b/core/modules/node/tests/modules/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml
new file mode 100644
index 0000000..34fd420
--- /dev/null
+++ b/core/modules/node/tests/modules/node_access_test_auto_bubbling/node_access_test_auto_bubbling.routing.yml
@@ -0,0 +1,6 @@
+node_access_test_auto_bubbling:
+  path: '/node_access_test_auto_bubbling'
+  defaults:
+    _controller: '\Drupal\node_access_test_auto_bubbling\Controller\NodeAccessTestAutoBubblingController::latest'
+  requirements:
+    _access: 'TRUE'
diff --git a/core/modules/node/tests/modules/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php b/core/modules/node/tests/modules/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php
new file mode 100644
index 0000000..4549f91
--- /dev/null
+++ b/core/modules/node/tests/modules/node_access_test_auto_bubbling/src/Controller/NodeAccessTestAutoBubblingController.php
@@ -0,0 +1,59 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\node_access_test_auto_bubbling\Controller\NodeAccessTestAutoBubblingController.
+ */
+
+namespace Drupal\node_access_test_auto_bubbling\Controller;
+
+use Drupal\Core\Controller\ControllerBase;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Entity\Query\QueryFactory;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Returns a node ID listing.
+ */
+class NodeAccessTestAutoBubblingController extends ControllerBase implements ContainerInjectionInterface {
+
+  /**
+   * The entity query factory service.
+   *
+   * @var \Drupal\Core\Entity\Query\QueryFactory
+   */
+  protected $entityQuery;
+
+  /**
+   * Constructs a new NodeAccessTestAutoBubblingController.
+   *
+   * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
+   *   The entity query factory.
+   */
+  public function __construct(QueryFactory $entity_query) {
+    $this->entityQuery = $entity_query;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity.query')
+    );
+  }
+
+  /**
+   * Lists the three latest node IDs.
+   */
+  public function latest() {
+    $nids = $this->entityQuery->get('node')
+      ->condition('status', NODE_PUBLISHED)
+      ->sort('created', 'DESC')
+      ->range(0, 3)
+      ->addTag('node_access')
+      ->execute();
+    return ['#markup' => $this->t('The five latest nodes are: !nids.', ['!nids' => implode(', ', $nids)])];
+  }
+
+}
