diff --git a/core/lib/Drupal/Core/Menu/LocalActionDefault.php b/core/lib/Drupal/Core/Menu/LocalActionDefault.php
index b0109a9..2eec86d 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionDefault.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionDefault.php
@@ -62,6 +62,27 @@ public static function create(ContainerInterface $container, array $configuratio
   /**
    * {@inheritdoc}
    */
+  public function getCacheContexts() {
+    return isset($this->pluginDefinition['cache']['contexts']) ? $this->pluginDefinition['cache']['contexts'] : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return isset($this->pluginDefinition['cache']['tags']) ? $this->pluginDefinition['cache']['tags'] : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return isset($this->pluginDefinition['cache']['max-age']) ? $this->pluginDefinition['cache']['max-age'] : Cache::PERMANENT;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getRouteName() {
     return $this->pluginDefinition['route_name'];
   }
diff --git a/core/lib/Drupal/Core/Menu/LocalActionInterface.php b/core/lib/Drupal/Core/Menu/LocalActionInterface.php
index 57d441a..b613901 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionInterface.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionInterface.php
@@ -7,12 +7,13 @@
 
 namespace Drupal\Core\Menu;
 
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\Routing\RouteMatchInterface;
 
 /**
  * Defines an interface for menu local actions.
  */
-interface LocalActionInterface {
+interface LocalActionInterface extends CacheableDependencyInterface {
 
   /**
    * Get the route name from the settings.
diff --git a/core/lib/Drupal/Core/Menu/LocalActionManager.php b/core/lib/Drupal/Core/Menu/LocalActionManager.php
index eb1e2d0..30f9068 100644
--- a/core/lib/Drupal/Core/Menu/LocalActionManager.php
+++ b/core/lib/Drupal/Core/Menu/LocalActionManager.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Menu;
 
 use Drupal\Core\Access\AccessManagerInterface;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
@@ -180,8 +181,10 @@ public function getActionsForRoute($route_appears) {
       }
     }
     $links = array();
+    $cacheable_metadata = new CacheableMetadata();
     /** @var $plugin \Drupal\Core\Menu\LocalActionInterface */
     foreach ($this->instances[$route_appears] as $plugin_id => $plugin) {
+      $cacheable_metadata = $cacheable_metadata->merge(CacheableMetadata::createFromObject($plugin));
       $route_name = $plugin->getRouteName();
       $route_parameters = $plugin->getRouteParameters($this->routeMatch);
       $links[$plugin_id] = array(
@@ -196,6 +199,8 @@ public function getActionsForRoute($route_appears) {
       );
     }
 
+    $cacheable_metadata->applyTo($links);
+
     return $links;
   }
 
diff --git a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php
index 6a09ffa..87f40f9 100644
--- a/core/lib/Drupal/Core/Menu/LocalTaskDefault.php
+++ b/core/lib/Drupal/Core/Menu/LocalTaskDefault.php
@@ -8,6 +8,8 @@
 namespace Drupal\Core\Menu;
 
 use Drupal\Component\Plugin\PluginBase;
+use Drupal\Core\Cache\Cache;
+use Drupal\Core\Cache\CacheableDependencyInterface;
 use Drupal\Core\DependencyInjection\DependencySerializationTrait;
 use Drupal\Core\Routing\RouteMatchInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -15,7 +17,7 @@
 /**
  * Default object used for LocalTaskPlugins.
  */
-class LocalTaskDefault extends PluginBase implements LocalTaskInterface {
+class LocalTaskDefault extends PluginBase implements LocalTaskInterface, CacheableDependencyInterface {
 
   use DependencySerializationTrait;
 
@@ -36,6 +38,27 @@ class LocalTaskDefault extends PluginBase implements LocalTaskInterface {
   /**
    * {@inheritdoc}
    */
+  public function getCacheContexts() {
+    return isset($this->pluginDefinition['cache']['contexts']) ? $this->pluginDefinition['cache']['contexts'] : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheTags() {
+    return isset($this->pluginDefinition['cache']['tags']) ? $this->pluginDefinition['cache']['tags'] : [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheMaxAge() {
+    return isset($this->pluginDefinition['cache']['max-age']) ? $this->pluginDefinition['cache']['max-age'] : Cache::PERMANENT;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function getRouteName() {
     return $this->pluginDefinition['route_name'];
   }
diff --git a/core/lib/Drupal/Core/Menu/Plugin/Block/LocalTasksBlock.php b/core/lib/Drupal/Core/Menu/Plugin/Block/LocalTasksBlock.php
index 6edfc6b..9bfea11 100644
--- a/core/lib/Drupal/Core/Menu/Plugin/Block/LocalTasksBlock.php
+++ b/core/lib/Drupal/Core/Menu/Plugin/Block/LocalTasksBlock.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core\Menu\Plugin\Block;
 
 use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\CacheableMetadata;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Menu\LocalTaskManagerInterface;
 use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
@@ -93,9 +94,13 @@ public function build() {
       '#theme' => 'menu_local_tasks',
     ];
 
+    $cacheable_metadata = new CacheableMetadata();
     // Add only selected levels for the printed output.
     if ($config['primary']) {
       $links = $this->localTaskManager->getLocalTasks($this->routeMatch->getRouteName(), 0);
+      foreach ($links as $link) {
+        $cacheable_metadata->merge(CacheableMetadata::createFromObject($link));
+      }
       // Do not display single tabs.
       $tabs += [
         '#primary' => count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : [],
@@ -103,6 +108,9 @@ public function build() {
     }
     if ($config['secondary']) {
       $links = $this->localTaskManager->getLocalTasks($this->routeMatch->getRouteName(), 1);
+      foreach ($links as $link) {
+        $cacheable_metadata->merge(CacheableMetadata::createFromObject($link));
+      }
       // Do not display single tabs.
       $tabs += [
         '#secondary' => count(Element::getVisibleChildren($links['tabs'])) > 1 ? $links['tabs'] : [],
@@ -113,6 +121,8 @@ public function build() {
       return [];
     }
 
+    $cacheable_metadata->applyTo($tabs);
+
     return $tabs;
   }
 
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
index 02d4578..ac0a66f 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionDefaultTest.php
@@ -66,6 +66,12 @@ class LocalActionDefaultTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
+    $this->pluginDefinition['cache'] = [
+      'contexts' => [$this->randomMachineName(), $this->randomMachineName()],
+      'tags' => [$this->randomMachineName(), $this->randomMachineName()],
+      'max-age' => mt_rand(),
+    ];
+
     $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
     $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
   }
@@ -78,6 +84,33 @@ protected function setupLocalActionDefault() {
   }
 
   /**
+   * @covers ::getCacheContexts
+   */
+  public function testGetCacheContexts() {
+    $this->setupLocalActionDefault();
+
+    $this->assertSame($this->pluginDefinition['cache']['contexts'], $this->localActionDefault->getCacheContexts());
+  }
+
+  /**
+   * @covers ::getCacheTags
+   */
+  public function testGetCacheTags() {
+    $this->setupLocalActionDefault();
+
+    $this->assertSame($this->pluginDefinition['cache']['tags'], $this->localActionDefault->getCacheTags());
+  }
+
+  /**
+   * @covers ::getCacheMaxAge
+   */
+  public function testGetCacheMaxAge() {
+    $this->setupLocalActionDefault();
+
+    $this->assertSame($this->pluginDefinition['cache']['max-age'], $this->localActionDefault->getCacheMaxAge());
+  }
+
+  /**
    * Tests the getTitle method without a translation context.
    *
    * @see \Drupal\Core\Menu\LocalTaskDefault::getTitle()
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
index 24a09f2..22d4aa5 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalActionManagerTest.php
@@ -12,7 +12,10 @@
 use Drupal\Core\Access\AccessManagerInterface;
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Access\AccessResultForbidden;
+use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Cache\Context\CacheContextsManager;
+use Drupal\Core\DependencyInjection\Container;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Menu\LocalActionManager;
@@ -153,6 +156,17 @@ public function testGetActionsForRoute($route_appears, array $plugin_definitions
     $map = array();
     foreach ($plugin_definitions as $plugin_id => $plugin_definition) {
       $plugin = $this->getMock('Drupal\Core\Menu\LocalActionInterface');
+      if (in_array($route_appears, $plugin_definition['appears_on'])) {
+        $plugin->expects($this->atLeastOnce())
+          ->method('getCacheContexts')
+          ->willReturn($plugin_definition['cache']['contexts']);
+        $plugin->expects($this->atLeastOnce())
+          ->method('getCacheTags')
+          ->willReturn($plugin_definition['cache']['tags']);
+        $plugin->expects($this->atLeastOnce())
+          ->method('getCacheMaxAge')
+          ->willReturn($plugin_definition['cache']['max-age']);
+      }
       $plugin->expects($this->any())
         ->method('getRouteName')
         ->will($this->returnValue($plugin_definition['route_name']));
@@ -180,10 +194,21 @@ public function testGetActionsForRoute($route_appears, array $plugin_definitions
       ->method('createInstance')
       ->will($this->returnValueMap($map));
 
+    $cache_contexts_manager = $this->prophesize(CacheContextsManager::class);
+
+    $container = new Container();
+    $container->set('cache_contexts_manager', $cache_contexts_manager->reveal());
+    \Drupal::setContainer($container);
+
     $this->assertEquals($expected_actions, $this->localActionManager->getActionsForRoute($route_appears));
   }
 
   public function getActionsForRouteProvider() {
+    $cache_contexts_1 = [$this->randomMachineName(), $this->randomMachineName()];
+    $cache_contexts_2 = [$this->randomMachineName(), $this->randomMachineName()];
+    $cache_tags_1 = [$this->randomMachineName(), $this->randomMachineName()];
+    $cache_tags_2 = [$this->randomMachineName(), $this->randomMachineName()];
+
     // Single available and single expected plugins.
     $data[] = array(
       'test_route',
@@ -195,6 +220,11 @@ public function getActionsForRouteProvider() {
           'route_name' => 'test_route_2',
           'title' => 'Plugin ID 1',
           'weight' => 0,
+          'cache' => [
+            'contexts' => $cache_contexts_1,
+            'tags' => $cache_tags_1,
+            'max-age' => 7,
+          ],
         ),
       ),
       array(
@@ -208,6 +238,11 @@ public function getActionsForRouteProvider() {
           '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
+        '#cache' => [
+          'contexts' => $cache_contexts_1,
+          'tags' => $cache_tags_1,
+          'max-age' => 7,
+        ],
       ),
     );
     // Multiple available and single expected plugins.
@@ -221,6 +256,11 @@ public function getActionsForRouteProvider() {
           'route_name' => 'test_route_2',
           'title' => 'Plugin ID 1',
           'weight' => 0,
+          'cache' => [
+            'contexts' => $cache_contexts_1,
+            'tags' => $cache_tags_1,
+            'max-age' => 7,
+          ],
         ),
         'plugin_id_2' => array(
           'appears_on' => array(
@@ -242,6 +282,11 @@ public function getActionsForRouteProvider() {
           '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
+        '#cache' => [
+          'contexts' => $cache_contexts_1,
+          'tags' => $cache_tags_1,
+          'max-age' => 7,
+        ],
       ),
     );
 
@@ -256,6 +301,11 @@ public function getActionsForRouteProvider() {
           'route_name' => 'test_route_2',
           'title' => 'Plugin ID 1',
           'weight' => 1,
+          'cache' => [
+            'contexts' => $cache_contexts_1,
+            'tags' => $cache_tags_1,
+            'max-age' => 7,
+          ],
         ),
         'plugin_id_2' => array(
           'appears_on' => array(
@@ -264,6 +314,11 @@ public function getActionsForRouteProvider() {
           'route_name' => 'test_route_3',
           'title' => 'Plugin ID 2',
           'weight' => 0,
+          'cache' => [
+            'contexts' => $cache_contexts_2,
+            'tags' => $cache_tags_2,
+            'max-age' => 0,
+          ],
         ),
       ),
       array(
@@ -287,6 +342,11 @@ public function getActionsForRouteProvider() {
           '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
+        '#cache' => [
+          'contexts' => array_merge($cache_contexts_1, $cache_contexts_2),
+          'tags' => array_merge($cache_tags_1, $cache_tags_2),
+          'max-age' => 0,
+        ],
       ),
     );
 
@@ -302,6 +362,11 @@ public function getActionsForRouteProvider() {
           'route_parameters' => array('test1'),
           'title' => 'Plugin ID 1',
           'weight' => 1,
+          'cache' => [
+            'contexts' => $cache_contexts_1,
+            'tags' => $cache_tags_1,
+            'max-age' => Cache::PERMANENT,
+          ],
         ),
         'plugin_id_2' => array(
           'appears_on' => array(
@@ -311,6 +376,11 @@ public function getActionsForRouteProvider() {
           'route_parameters' => array('test2'),
           'title' => 'Plugin ID 2',
           'weight' => 0,
+          'cache' => [
+            'contexts' => $cache_contexts_2,
+            'tags' => $cache_tags_2,
+            'max-age' => Cache::PERMANENT,
+          ],
         ),
       ),
       array(
@@ -334,6 +404,11 @@ public function getActionsForRouteProvider() {
           '#access' => AccessResult::forbidden(),
           '#weight' => 0,
         ),
+        '#cache' => [
+          'contexts' => array_merge($cache_contexts_1, $cache_contexts_2),
+          'tags' => array_merge($cache_tags_1, $cache_tags_2),
+          'max-age' => Cache::PERMANENT,
+        ],
       ),
     );
 
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
index 6e393a8..1399c0c 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskDefaultTest.php
@@ -67,6 +67,12 @@ class LocalTaskDefaultTest extends UnitTestCase {
   protected function setUp() {
     parent::setUp();
 
+    $this->pluginDefinition['cache'] = [
+      'contexts' => [$this->randomMachineName(), $this->randomMachineName()],
+      'tags' => [$this->randomMachineName(), $this->randomMachineName()],
+      'max-age' => mt_rand(),
+    ];
+
     $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
     $this->routeProvider = $this->getMock('Drupal\Core\Routing\RouteProviderInterface');
   }
@@ -81,6 +87,33 @@ protected function setupLocalTaskDefault() {
   }
 
   /**
+   * @covers ::getCacheContexts
+   */
+  public function testGetCacheContexts() {
+    $this->setupLocalTaskDefault();
+
+    $this->assertSame($this->pluginDefinition['cache']['contexts'], $this->localTaskBase->getCacheContexts());
+  }
+
+  /**
+   * @covers ::getCacheTags
+   */
+  public function testGetCacheTags() {
+    $this->setupLocalTaskDefault();
+
+    $this->assertSame($this->pluginDefinition['cache']['tags'], $this->localTaskBase->getCacheTags());
+  }
+
+  /**
+   * @covers ::getCacheMaxAge
+   */
+  public function testGetCacheMaxAge() {
+    $this->setupLocalTaskDefault();
+
+    $this->assertSame($this->pluginDefinition['cache']['max-age'], $this->localTaskBase->getCacheMaxAge());
+  }
+
+  /**
    * @covers ::getRouteParameters
    */
   public function testGetRouteParametersForStaticRoute() {
