diff --git a/core/modules/node/node.post_update.php b/core/modules/node/node.post_update.php
index 82d1f41fd3..adb2397584 100644
--- a/core/modules/node/node.post_update.php
+++ b/core/modules/node/node.post_update.php
@@ -14,3 +14,9 @@ function node_removed_post_updates() {
     'node_post_update_node_revision_views_data' => '9.0.0',
   ];
 }
+
+/**
+ * Rebuild the node revision routes.
+ */
+function node_post_update_rebuild_node_revision_routes() {
+}
diff --git a/core/modules/node/node.routing.yml b/core/modules/node/node.routing.yml
index ac958ea242..59d34f9b69 100644
--- a/core/modules/node/node.routing.yml
+++ b/core/modules/node/node.routing.yml
@@ -59,6 +59,8 @@ entity.node.version_history:
     _access_node_revision: 'view'
     node: \d+
   options:
+    node:
+      type: entity:node
     _node_operation_route: TRUE
 
 entity.node.revision:
@@ -69,6 +71,12 @@ entity.node.revision:
   requirements:
     _access_node_revision: 'view'
     node: \d+
+  options:
+    parameters:
+      node:
+        type: entity:node
+      node_revision:
+        type: entity_revision:node
 
 node.revision_revert_confirm:
   path: '/node/{node}/revisions/{node_revision}/revert'
@@ -80,6 +88,11 @@ node.revision_revert_confirm:
     node: \d+
   options:
     _node_operation_route: TRUE
+    parameters:
+      node:
+        type: entity:node
+      node_revision:
+        type: entity_revision:node
 
 node.revision_revert_translation_confirm:
   path: '/node/{node}/revisions/{node_revision}/revert/{langcode}'
@@ -91,6 +104,11 @@ node.revision_revert_translation_confirm:
     node: \d+
   options:
     _node_operation_route: TRUE
+    parameters:
+      node:
+        type: entity:node
+      node_revision:
+        type: entity_revision:node
 
 node.revision_delete_confirm:
   path: '/node/{node}/revisions/{node_revision}/delete'
@@ -102,6 +120,11 @@ node.revision_delete_confirm:
     node: \d+
   options:
     _node_operation_route: TRUE
+    parameters:
+      node:
+        type: entity:node
+      node_revision:
+        type: entity_revision:node
 
 entity.node_type.collection:
   path: '/admin/structure/types'
diff --git a/core/modules/node/src/Controller/NodeController.php b/core/modules/node/src/Controller/NodeController.php
index d87b298869..6bee7f9f09 100644
--- a/core/modules/node/src/Controller/NodeController.php
+++ b/core/modules/node/src/Controller/NodeController.php
@@ -112,15 +112,14 @@ public function addPage() {
   /**
    * Displays a node revision.
    *
-   * @param int $node_revision
-   *   The node revision ID.
+   * @param \Drupal\node\NodeInterface $node_revision
+   *   The node revision.
    *
    * @return array
    *   An array suitable for \Drupal\Core\Render\RendererInterface::render().
    */
-  public function revisionShow($node_revision) {
-    $node = $this->entityTypeManager()->getStorage('node')->loadRevision($node_revision);
-    $node = $this->entityRepository->getTranslationFromContext($node);
+  public function revisionShow(NodeInterface $node_revision) {
+    $node = $this->entityRepository->getTranslationFromContext($node_revision);
     $node_view_controller = new NodeViewController($this->entityTypeManager(), $this->renderer, $this->currentUser(), $this->entityRepository);
     $page = $node_view_controller->view($node);
     unset($page['nodes'][$node->id()]['#cache']);
@@ -130,14 +129,14 @@ public function revisionShow($node_revision) {
   /**
    * Page title callback for a node revision.
    *
-   * @param int $node_revision
-   *   The node revision ID.
+   * @param \Drupal\node\NodeInterface $node_revision
+   *   The node revision.
    *
    * @return string
    *   The page title.
    */
-  public function revisionPageTitle($node_revision) {
-    $node = $this->entityTypeManager()->getStorage('node')->loadRevision($node_revision);
+  public function revisionPageTitle(NodeInterface $node_revision) {
+    $node = $this->entityRepository->getTranslationFromContext($node_revision);
     return $this->t('Revision of %title from %date', ['%title' => $node->label(), '%date' => $this->dateFormatter->format($node->getRevisionCreationTime())]);
   }
 
diff --git a/core/modules/node/src/Form/NodeRevisionDeleteForm.php b/core/modules/node/src/Form/NodeRevisionDeleteForm.php
index aefefde6cd..8d4554152c 100644
--- a/core/modules/node/src/Form/NodeRevisionDeleteForm.php
+++ b/core/modules/node/src/Form/NodeRevisionDeleteForm.php
@@ -8,6 +8,7 @@
 use Drupal\Core\Form\ConfirmFormBase;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Url;
+use Drupal\node\NodeInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -117,8 +118,8 @@ public function getConfirmText() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, $node_revision = NULL) {
-    $this->revision = $this->nodeStorage->loadRevision($node_revision);
+  public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node_revision = NULL) {
+    $this->revision = $node_revision;
     $form = parent::buildForm($form, $form_state);
 
     return $form;
diff --git a/core/modules/node/src/Form/NodeRevisionRevertForm.php b/core/modules/node/src/Form/NodeRevisionRevertForm.php
index da5cba023c..94a8f3b1a0 100644
--- a/core/modules/node/src/Form/NodeRevisionRevertForm.php
+++ b/core/modules/node/src/Form/NodeRevisionRevertForm.php
@@ -111,8 +111,8 @@ public function getDescription() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, $node_revision = NULL) {
-    $this->revision = $this->nodeStorage->loadRevision($node_revision);
+  public function buildForm(array $form, FormStateInterface $form_state, NodeInterface $node_revision = NULL) {
+    $this->revision = $node_revision;
     $form = parent::buildForm($form, $form_state);
 
     return $form;
diff --git a/core/modules/node/tests/modules/node_routes_test/node_routes_test.info.yml b/core/modules/node/tests/modules/node_routes_test/node_routes_test.info.yml
new file mode 100644
index 0000000000..d690772973
--- /dev/null
+++ b/core/modules/node/tests/modules/node_routes_test/node_routes_test.info.yml
@@ -0,0 +1,7 @@
+name: 'Node routes test'
+type: module
+description: 'Module for testing routes.'
+package: Testing
+version: VERSION
+dependecies:
+  - node
diff --git a/core/modules/node/tests/modules/node_routes_test/src/Plugin/Block/NodeRoutesTestBlock.php b/core/modules/node/tests/modules/node_routes_test/src/Plugin/Block/NodeRoutesTestBlock.php
new file mode 100644
index 0000000000..506bc41f90
--- /dev/null
+++ b/core/modules/node/tests/modules/node_routes_test/src/Plugin/Block/NodeRoutesTestBlock.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace Drupal\node_routes_test\Plugin\Block;
+
+use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Provides a testing block for node routes.
+ *
+ * @Block(
+ *  id = "node_routes_test_block",
+ *  admin_label = @Translation("Node routes test block")
+ * )
+ */
+class NodeRoutesTestBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The current route match.
+   *
+   * @var \Drupal\Core\Routing\RouteMatchInterface
+   */
+  protected $routeMatch;
+
+  /**
+   * Creates a NodeRoutesTestBlock instance.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param mixed $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Routing\RouteMatchInterface $route_match
+   *   The current route match.
+   */
+  public function __construct(array $configuration, $plugin_id, $plugin_definition, RouteMatchInterface $route_match) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->routeMatch = $route_match;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('current_route_match')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function build() {
+    $cacheable_metadata = CacheableMetadata::createFromObject($this)
+      ->addCacheContexts(['route']);
+    $article_page_markup = 'A page without node';
+    $revision_page_markup = ' and without revision';
+    // Upcasted node object.
+    if ($node = $this->routeMatch->getParameter('node')) {
+      $article_page_markup = sprintf('A page with node: (%s)', $node->id());
+      $cacheable_metadata->addCacheableDependency($node);
+    }
+    // Upcasted node revision object.
+    if ($node_revision = $this->routeMatch->getParameter('node_revision')) {
+      $revision_page_markup = sprintf(' and node revision: (%s)', $node_revision->getRevisionId());
+    }
+
+    $build = [
+      '#markup' => $article_page_markup . $revision_page_markup,
+    ];
+    // Apply cacheability.
+    $cacheable_metadata->applyTo($build);
+    return $build;
+  }
+
+}
diff --git a/core/modules/node/tests/src/Functional/NodeRoutesTest.php b/core/modules/node/tests/src/Functional/NodeRoutesTest.php
new file mode 100644
index 0000000000..b12165a0b3
--- /dev/null
+++ b/core/modules/node/tests/src/Functional/NodeRoutesTest.php
@@ -0,0 +1,91 @@
+<?php
+
+namespace Drupal\Tests\node\Functional;
+
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\node\Entity\Node;
+use Drupal\node\NodeInterface;
+
+/**
+ * Provides tests for node routes.
+ *
+ * @group node
+ */
+class NodeRoutesTest extends NodeTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultTheme = 'stark';
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = [
+    'block',
+    'content_translation',
+    'language',
+    'node_routes_test',
+  ];
+
+  /**
+   * Tests up-casting for revision routes.
+   */
+  public function testRevisionRoutes() {
+    ConfigurableLanguage::createFromLangcode('it')->save();
+
+    $this->drupalPlaceBlock('node_routes_test_block', [
+      'visibility' => [
+        'node_type' => [
+          'bundles' => [
+            'article' => 'article',
+          ],
+          'negate' => FALSE,
+          'context_mapping' => [
+            'node' => '@node.node_route_context:node',
+          ],
+        ],
+      ],
+    ]);
+
+    $account = $this->drupalCreateUser([
+      'view article revisions',
+      'revert article revisions',
+      'delete article revisions',
+      'edit any article content',
+      'delete any article content',
+      'translate any entity',
+    ]);
+    $this->drupalLogin($account);
+
+    $node = Node::create([
+      'type' => 'article',
+      'title' => 'Foo',
+      'status' => NodeInterface::PUBLISHED,
+    ]);
+    $node->save();
+    $nid = $node->id();
+    $initial_rid = $node->getRevisionId();
+
+    $node->setTitle('Bar');
+    $node->setNewRevision(TRUE);
+    $node->save();
+    $current_rid = $node->getRevisionId();
+
+    $this->drupalGet("node/$nid/revisions");
+    $this->assertSession()->pageTextContainsOnce(sprintf('A page with node: (%s) and without revision', $nid));
+
+    $this->drupalGet("node/$nid/revisions/$current_rid/view");
+    $this->assertSession()->pageTextContainsOnce(sprintf('A page with node: (%s) and node revision: (%s)', $nid, $current_rid));
+
+    $this->drupalGet("node/$nid/revisions/$initial_rid/revert");
+    $this->assertSession()->pageTextContainsOnce(sprintf('A page with node: (%s) and node revision: (%s)', $nid, $initial_rid));
+
+    $this->drupalGet("node/$nid/revisions/$initial_rid/revert/it");
+    $this->assertSession()->pageTextContainsOnce(sprintf('A page with node: (%s) and node revision: (%s)', $nid, $initial_rid));
+
+    $this->drupalGet("node/$nid/revisions/$initial_rid/delete");
+    $this->assertSession()->pageTextContainsOnce(sprintf('A page with node: (%s) and node revision: (%s)', $nid, $initial_rid));
+  }
+
+}
