diff --git a/core/modules/book/src/BookManager.php b/core/modules/book/src/BookManager.php
index 60754be..8d360b3 100644
--- a/core/modules/book/src/BookManager.php
+++ b/core/modules/book/src/BookManager.php
@@ -933,12 +933,10 @@ public function bookTreeCheckAccess(&$tree, $node_links = array()) {
 
       // @todo This should be actually filtering on the desired node status
       //   field language and just fall back to the default language.
-      $nids = \Drupal::entityQuery('node')
-        ->condition('nid', $nids, 'IN')
-        ->condition('status', 1)
-        ->execute();
+      $book_links = $this->bookOutlineStorage->loadMultiple($nids);
 
-      foreach ($nids as $nid) {
+      foreach ($book_links as $book_link) {
+        $nid = $book_link['nid'];
         foreach ($node_links[$nid] as $mlid => $link) {
           $node_links[$nid][$mlid]['access'] = TRUE;
         }
@@ -979,11 +977,11 @@ protected function doBookTreeCheckAccess(&$tree) {
    */
   public function bookLinkTranslate(&$link) {
     $node = NULL;
-    // Access will already be set in the tree functions.
-    if (!isset($link['access'])) {
-      $node = $this->entityManager->getStorage('node')->load($link['nid']);
-      $link['access'] = $node && $node->access('view');
-    }
+    // Check access via the api, since the query node_access tag doesn't
+    // check for unpublished nodes.
+    $node = $this->entityManager->getStorage('node')->load($link['nid']);
+    $link['access'] = $node && $node->access('view');
+
     // For performance, don't localize a link the user can't access.
     if ($link['access']) {
       // @todo - load the nodes en-mass rather than individually.
diff --git a/core/modules/book/src/Tests/BookTest.php b/core/modules/book/src/Tests/BookTest.php
index 0158992..7f61892 100644
--- a/core/modules/book/src/Tests/BookTest.php
+++ b/core/modules/book/src/Tests/BookTest.php
@@ -39,14 +39,14 @@ class BookTest extends WebTestBase {
   /**
    * A user with permission to view a book and access printer-friendly version.
    *
-   * @var object
+   * @var \Drupal\user\UserInterface
    */
   protected $webUser;
 
   /**
    * A user with permission to create and edit books and to administer blocks.
    *
-   * @var object
+   * @var \Drupal\user\UserInterface
    */
   protected $adminUser;
 
@@ -436,6 +436,31 @@ function testBookNavigationBlock() {
     $this->assertText($block->label(), 'Book navigation block is displayed.');
     $this->assertText($this->book->label(), format_string('Link to book root (@title) is displayed.', array('@title' => $nodes[0]->label())));
     $this->assertNoText($nodes[0]->label(), 'No links to individual book pages are displayed.');
+
+    // Ensure that an unpublished node does not appear in the navigation for a
+    // user without access. By unpublishing a parent page, child pages should
+    // not appear in the navigation. The node_access_test module is disabled
+    // since it interfers with this logic.
+
+    /** @var \Drupal\Core\Extension\ModuleInstaller $installer */
+    $installer = \Drupal::service('module_installer');
+    $installer->uninstall(['node_access_test']);
+    node_access_rebuild();
+
+    $nodes[0]->setPublished(FALSE);
+    $nodes[0]->save();
+
+    // Verify the user does not have access to the unpublished node.
+    $this->assertFalse($nodes[0]->access('view', $this->webUser));
+
+    // Verify the unpublished book page does not appear in the navigation.
+    $this->drupalLogin($this->webUser);
+    $this->drupalGet($nodes[0]->urlInfo());
+    $this->assertResponse(403);
+    $this->drupalGet($this->book->urlInfo());
+    $this->assertNoText($nodes[0]->getTitle(), 'Unpublished book page does not appear in the navigation for users without access.');
+    $this->assertNoText($nodes[1]->getTitle(), 'Published child page does not appear below an unpublished parent.');
+    $this->assertNoText($nodes[2]->getTitle(), 'Published child page does not appear below an unpublished parent.');
   }
 
   /**
@@ -680,7 +705,7 @@ public function testSaveBookLink() {
    */
   public function testBookListing() {
     // Create a new book.
-    $this->createBook();
+    $nodes = $this->createBook();
 
     // Must be a user with 'node test view' permission since node_access_test is installed.
     $this->drupalLogin($this->webUser);
@@ -696,7 +721,7 @@ public function testBookListing() {
    */
   public function testAdminBookListing() {
     // Create a new book.
-    $this->createBook();
+    $nodes = $this->createBook();
 
     // Load the book page and assert the created book title is displayed.
     $this->drupalLogin($this->adminUser);
@@ -709,7 +734,7 @@ public function testAdminBookListing() {
    */
   public function testAdminBookNodeListing() {
     // Create a new book.
-    $this->createBook();
+    $nodes = $this->createBook();
     $this->drupalLogin($this->adminUser);
 
     // Load the book page list and assert the created book title is displayed
@@ -719,6 +744,29 @@ public function testAdminBookNodeListing() {
 
     $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a');
     $this->assertEqual((string) $elements[0], 'View', 'View link is found from the list.');
+    $this->assertEqual(count($nodes), count($elements), 'All the book pages are displayed on the book outline page.');
+
+    // Unpublish a book in the hierarchy.
+    $nodes[0]->setPublished(FALSE);
+    $nodes[0]->save();
+
+    // Node should still appear on the outline for admins.
+    $this->drupalGet('admin/structure/book/' . $this->book->id());
+    $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a');
+    $this->assertEqual(count($nodes), count($elements), 'All the book pages are displayed on the book outline page.');
+
+    // Saving a book page not as the current version shouldn't effect the book.
+    $old_title = $nodes[1]->getTitle();
+    $new_title = $this->randomGenerator->name();
+    $nodes[1]->isDefaultRevision(FALSE);
+    $nodes[1]->setNewRevision(TRUE);
+    $nodes[1]->setTitle($new_title);
+    $nodes[1]->save();
+    $this->drupalGet('admin/structure/book/' . $this->book->id());
+    $elements = $this->xpath('//table//ul[@class="dropbutton"]/li/a');
+    $this->assertEqual(count($nodes), count($elements), 'All the book pages are displayed on the book outline page.');
+    $this->assertNoText($new_title, 'The revision that is not the default revision does not appear.');
+    $this->assertText($old_title, 'The default revision title appears.');
   }
 
   /**
diff --git a/core/modules/book/tests/src/Kernel/Migrate/d6/MigrateBookTest.php b/core/modules/book/tests/src/Kernel/Migrate/d6/MigrateBookTest.php
index ffcc3fe..ca022e2 100644
--- a/core/modules/book/tests/src/Kernel/Migrate/d6/MigrateBookTest.php
+++ b/core/modules/book/tests/src/Kernel/Migrate/d6/MigrateBookTest.php
@@ -2,6 +2,7 @@
 
 namespace Drupal\Tests\book\Kernel\Migrate\d6;
 
+use Drupal\Core\Session\AccountInterface;
 use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
 use Drupal\node\Entity\Node;
 
@@ -48,6 +49,13 @@ public function testBook() {
     $this->assertIdentical('8', $nodes[8]->book['bid']);
     $this->assertIdentical('0', $nodes[8]->book['pid']);
 
+    // Access is checked when building book trees, so grant the anonymous user
+    // role 'access content' permission.
+    /** @var \Drupal\user\RoleInterface $role */
+    $role = $this->container->get('entity_type.manager')->getStorage('user_role')->load(AccountInterface::ANONYMOUS_ROLE);
+    $role->grantPermission('access content');
+    $role->save();
+
     $tree = \Drupal::service('book.manager')->bookTreeAllData(4);
     $this->assertIdentical('4', $tree['49990 Node 4 4']['link']['nid']);
     $this->assertIdentical('5', $tree['49990 Node 4 4']['below']['50000 Node 5 5']['link']['nid']);
