How do I correctly setup caching for my custom block showing content depending on the current node? I have this very basic block which just shows the current node's ID.

&lt;?php

/**
 * @file
 * Contains \Drupal\mymodule\Plugin\Block\ExampleEmptyBlock.
 */

namespace Drupal\mymodule\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;

/**
 * @Block(
 *   id = "example_empty",
 *   admin_label = @Translation("Example: empty block")
 * )
 */
class ExampleEmptyBlock extends BlockBase {

  /**
   * {@inheritdoc}
   */
  public function build() {
    $node = \Drupal::routeMatch()-&gt;getParameter('node');
    $build = array();

    if ($node) {
      $config = \Drupal::config('system.site');

      $build = array(
        '#type' =&gt; 'markup',
        '#markup' =&gt; '&lt;p&gt;' . $node-&gt;id() . '&lt;p&gt;',
        '#cache' =&gt; array(
          'tags' =&gt; $this-&gt;getCacheTags(),
          'contexts' =&gt; $this-&gt;getCacheContexts(),
        ),
      );
    }

    return $build;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    $node = \Drupal::routeMatch()-&gt;getParameter('node');
    return Cache::mergeTags(parent::getCacheTags(), ["node:{$node-&gt;id()}"]);
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), ['user.node_grants:view']);
  }

}


But once cached, the block stays the same, regardless which node I visit. How do I correctly cache the result per node ID?
This is full working code with comments.

namespace Drupal\module_name\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;

/**
 * Provides a Node cached block that display node's ID.
 *
 * @Block(
 *   id = "node_cached_block",
 *   admin_label = @Translation("Node Cached")
 * )
 */
class NodeCachedBlock extends BlockBase {
  public function build() {
    $build = array();
    //if node is found from routeMatch create a markup with node ID's.
    if ($node = \Drupal::routeMatch()-&gt;getParameter('node')) {
      $build['node_id'] = array(
        '#markup' =&gt; '&lt;p&gt;' . $node-&gt;id() . '&lt;p&gt;',
      );
    }
    return $build;
  }

  public function getCacheTags() {
    //With this when your node change your block will rebuild
    if ($node = \Drupal::routeMatch()-&gt;getParameter('node')) {
      //if there is node add its cachetag
      return Cache::mergeTags(parent::getCacheTags(), array('node:' . $node-&gt;id()));
    } else {
      //Return default tags instead.
      return parent::getCacheTags();
    }
  }

  public function getCacheContexts() {
    //if you depends on \Drupal::routeMatch()
    //you must set context of this block with 'route' context tag.
    //Every new route this block will rebuild
    return Cache::mergeContexts(parent::getCacheContexts(), array('route'));
  }
}


I tested it; it works.

Just put the code in a file named NodeCachedBlock.php in your module folder, change its namespace {module_name}, clear the cache and use it.