diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index a9ae1bdcce3662cd36456da1493622a786c7b25e..b3410649545b19726604108c4e834e5c0aefa571 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -688,18 +688,24 @@ public function prepareForm($form_id, &$form, FormStateInterface &$form_state) {
     if ($form_state->isProgrammed() || (isset($form['#token']) && $form['#token'] === FALSE)) {
       unset($form['#token']);
     }
-    elseif ($user && $user->isAuthenticated()) {
-      // Generate a public token based on the form id.
-      $form['#token'] = $form_id;
-      $form['form_token'] = array(
-        '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
-        '#type' => 'token',
-        '#default_value' => $this->csrfToken->get($form['#token']),
-        // Form processing and validation requires this value, so ensure the
-        // submitted form value appears literally, regardless of custom #tree
-        // and #parents being set elsewhere.
-        '#parents' => array('form_token'),
-      );
+    else {
+      $form['#cache']['contexts'][] = 'user.roles:authenticated';
+      if ($user && $user->isAuthenticated()) {
+        // Generate a public token based on the form id.
+        $form['#token'] = $form_id;
+        $form['form_token'] = array(
+          '#id' => Html::getUniqueId('edit-' . $form_id . '-form-token'),
+          '#type' => 'token',
+          '#default_value' => $this->csrfToken->get($form['#token']),
+          // Form processing and validation requires this value, so ensure the
+          // submitted form value appears literally, regardless of custom #tree
+          // and #parents being set elsewhere.
+          '#parents' => array('form_token'),
+          '#cache' => [
+            'max-age' => 0,
+          ],
+        );
+      }
     }
 
     if (isset($form_id)) {
diff --git a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
index d17fc3bf4edf445f7d9cfda6b20869a143ddb456..4ce15655badfe0d00d84ec075f9a6cdfed54a5a4 100644
--- a/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
+++ b/core/modules/block_content/src/Tests/BlockContentTranslationUITest.php
@@ -30,6 +30,17 @@ class BlockContentTranslationUITest extends ContentTranslationUITestBase {
     'block_content'
   );
 
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultCacheContexts = [
+    'languages:language_interface',
+    'theme',
+    'url.query_args:_wrapper_format',
+    'user.permissions',
+    'user.roles:authenticated',
+  ];
+
   /**
    * Overrides \Drupal\simpletest\WebTestBase::setUp().
    */
diff --git a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
index 3569a457d3c6f6c89cfdc91b3f07fb52b8cc2d00..43fd7798ed39ef136d32e0d78aeb1b4433d55cc0 100644
--- a/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
+++ b/core/modules/content_translation/src/Tests/ContentTestTranslationUITest.php
@@ -26,6 +26,17 @@ class ContentTestTranslationUITest extends ContentTranslationUITestBase {
    */
   public static $modules = array('language', 'content_translation', 'entity_test');
 
+  /**
+   * {@inheritdoc}
+   */
+  protected $defaultCacheContexts = [
+    'languages:language_interface',
+    'theme',
+    'url.query_args:_wrapper_format',
+    'user.permissions',
+    'user.roles:authenticated',
+  ];
+
   /**
    * Overrides \Drupal\simpletest\WebTestBase::setUp().
    */
diff --git a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php
index 1427c5de50816e24d229a69e9feb4a769ac3bb5a..f1ef4c51aa84a9aeed36e15849386abfabe863f3 100644
--- a/core/modules/views/src/Tests/Plugin/ExposedFormTest.php
+++ b/core/modules/views/src/Tests/Plugin/ExposedFormTest.php
@@ -207,6 +207,7 @@ public function testExposedSortAndItemsPerPage() {
       'entity_test_view_grants',
       'theme',
       'url.query_args',
+      'user.roles:authenticated',
       'languages:language_content'
     ];
 
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index 86b1fcb281d4b9426a057825d61f4b10c76c5f6e..d95e3bcb6f64fbdca4d6bca8357411248c21cb5c 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -18,6 +18,7 @@
 use Drupal\Core\Form\FormState;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Session\AccountProxyInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\RequestStack;
@@ -767,6 +768,65 @@ public function providerTestInvalidToken() {
     return $data;
   }
 
+  /**
+   * @covers ::prepareForm
+   *
+   * @dataProvider providerTestFormTokenCacheability
+   */
+  function testFormTokenCacheability($token, $is_authenticated, $expected_form_cacheability, $expected_token_cacheability) {
+    $user = $this->prophesize(AccountProxyInterface::class);
+    $user->isAuthenticated()
+      ->willReturn($is_authenticated);
+    $this->container->set('current_user', $user->reveal());
+    \Drupal::setContainer($this->container);
+
+    $form_id = 'test_form_id';
+    $form = $form_id();
+
+    if (isset($token)) {
+      $form['#token'] = $token;
+    }
+
+    $form_arg = $this->getMock('Drupal\Core\Form\FormInterface');
+    $form_arg->expects($this->once())
+      ->method('getFormId')
+      ->will($this->returnValue($form_id));
+    $form_arg->expects($this->once())
+      ->method('buildForm')
+      ->will($this->returnValue($form));
+
+    $form_state = new FormState();
+    $built_form = $this->formBuilder->buildForm($form_arg, $form_state);
+    if (!isset($expected_form_cacheability)) {
+      $this->assertFalse(isset($built_form['#cache']));
+    }
+    else {
+      $this->assertTrue(isset($built_form['#cache']));
+      $this->assertEquals($expected_form_cacheability, $built_form['#cache']);
+    }
+    if (!isset($expected_token_cacheability)) {
+      $this->assertFalse(isset($built_form['form_token']));
+    }
+    else {
+      $this->assertTrue(isset($built_form['form_token']));
+      $this->assertEquals($expected_token_cacheability, $built_form['form_token']['#cache']);
+    }
+  }
+
+  /**
+   * Data provider for testFormTokenCacheability.
+   *
+   * @return array
+   */
+  function providerTestFormTokenCacheability() {
+    return [
+      'token:none,authenticated:true' => [NULL, TRUE, ['contexts' => ['user.roles:authenticated']], ['max-age' => 0]],
+      'token:false,authenticated:true' => [FALSE, TRUE, NULL, NULL],
+      'token:none,authenticated:false' => [NULL, FALSE, ['contexts' => ['user.roles:authenticated']], NULL],
+      'token:false,authenticated:false' => [FALSE, FALSE, NULL, NULL],
+    ];
+  }
+
 }
 
 class TestForm implements FormInterface {