diff --git a/core/modules/block/src/BlockViewBuilder.php b/core/modules/block/src/BlockViewBuilder.php
index f33092de869e68bc07e128d2bf59960276fdbff4..1b77690e81518db25be9a5d162daa6981ef36ec1 100644
--- a/core/modules/block/src/BlockViewBuilder.php
+++ b/core/modules/block/src/BlockViewBuilder.php
@@ -6,6 +6,7 @@
 use Drupal\Core\Block\TitleBlockPluginInterface;
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Cache\CacheableMetadata;
+use Drupal\Core\Cache\CacheOptionalInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
 use Drupal\Core\Entity\EntityInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
@@ -50,7 +51,6 @@ public function viewMultiple(array $entities = [], $view_mode = 'full', $langcod
       // @see template_preprocess_block().
       $build[$entity_id] = [
         '#cache' => [
-          'keys' => ['entity_view', 'block', $entity->id()],
           'contexts' => Cache::mergeContexts(
             $entity->getCacheContexts(),
             $plugin->getCacheContexts()
@@ -61,6 +61,12 @@ public function viewMultiple(array $entities = [], $view_mode = 'full', $langcod
         '#weight' => $entity->getWeight(),
       ];
 
+      // Only add cache keys if the block plugin does not implement
+      // CacheOptionalInterface.
+      if (!$plugin instanceof CacheOptionalInterface) {
+        $build[$entity_id]['#cache']['keys'] = ['entity_view', 'block', $entity->id()];
+      }
+
       // Allow altering of cacheability metadata or setting #create_placeholder.
       $this->moduleHandler->alter(['block_build', "block_build_" . $plugin->getBaseId()], $build[$entity_id], $plugin);
 
diff --git a/core/modules/language/src/Plugin/Block/LanguageBlock.php b/core/modules/language/src/Plugin/Block/LanguageBlock.php
index 8b146db4adbd31b2f96328a9148e3be1b5b611c2..a9b3c1d5d35d7fc7329f2ef1154b6d926d5134e5 100644
--- a/core/modules/language/src/Plugin/Block/LanguageBlock.php
+++ b/core/modules/language/src/Plugin/Block/LanguageBlock.php
@@ -5,6 +5,7 @@
 use Drupal\Core\Access\AccessResult;
 use Drupal\Core\Block\Attribute\Block;
 use Drupal\Core\Block\BlockBase;
+use Drupal\Core\Cache\CacheOptionalInterface;
 use Drupal\Core\Path\PathMatcherInterface;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\Language\LanguageManagerInterface;
@@ -23,7 +24,7 @@
   category: new TranslatableMarkup("System"),
   deriver: LanguageBlockDeriver::class
 )]
-class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface {
+class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface, CacheOptionalInterface {
 
   /**
    * The language manager.
@@ -113,16 +114,18 @@ public function build() {
         '#set_active_class' => TRUE,
       ];
     }
+
+    // Add cache contexts for things that might cause links to change.
+    $build['#cache']['contexts'] = ['user.permissions', 'url.path', 'url.query_args', 'languages:' . $this->getDerivativeId()];
+
     return $build;
   }
 
   /**
    * {@inheritdoc}
-   *
-   * @todo Make cacheable in https://www.drupal.org/node/2232375.
    */
-  public function getCacheMaxAge() {
-    return 0;
+  public function createPlaceholder(): bool {
+    return TRUE;
   }
 
 }