diff --git a/core/core.services.yml b/core/core.services.yml
index 882cf9f..65fa4cb 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -115,7 +115,7 @@ services:
     arguments: [default]
   form_builder:
     class: Drupal\Core\Form\FormBuilder
-    arguments: ['@module_handler', '@keyvalue.expirable', '@event_dispatcher', '@url_generator', '@string_translation', '@?csrf_token', '@?http_kernel']
+    arguments: ['@module_handler', '@keyvalue.expirable', '@event_dispatcher', '@url_generator', '@config.factory', '@string_translation', '@?csrf_token', '@?http_kernel']
     calls:
       - [setRequest, ['@?request']]
   keyvalue:
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 9159f71..9a2b5dd 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -461,6 +461,7 @@ function install_begin_request(&$install_state) {
       ->addArgument(new Reference('keyvalue.expirable'))
       ->addArgument(new Reference('event_dispatcher'))
       ->addArgument(new Reference('url_generator'))
+      ->addArgument(new Reference('config.factory'))
       ->addArgument(new Reference('string_translation'))
       ->addArgument(new Reference('csrf_token', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
       ->addArgument(new Reference('http_kernel', ContainerInterface::IGNORE_ON_INVALID_REFERENCE))
diff --git a/core/lib/Drupal/Core/Form/FormBuilder.php b/core/lib/Drupal/Core/Form/FormBuilder.php
index de36029..a4e8496 100644
--- a/core/lib/Drupal/Core/Form/FormBuilder.php
+++ b/core/lib/Drupal/Core/Form/FormBuilder.php
@@ -12,6 +12,7 @@
 use Drupal\Component\Utility\Unicode;
 use Drupal\Component\Utility\Url;
 use Drupal\Core\Access\CsrfTokenGenerator;
+use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\HttpKernel;
 use Drupal\Core\KeyValueStore\KeyValueExpirableFactory;
@@ -59,6 +60,13 @@ class FormBuilder implements FormBuilderInterface {
   protected $urlGenerator;
 
   /**
+   * The config factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
    * The translation manager service.
    *
    * @var \Drupal\Core\StringTranslation\TranslationInterface
@@ -125,6 +133,11 @@ class FormBuilder implements FormBuilderInterface {
   protected $validatedForms = array();
 
   /**
+   * A reasonable minimum cache TTL for form cache entries.
+   */
+  const MIN_CACHE_TTL = 21600;
+
+  /**
    * Constructs a new FormBuilder.
    *
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -135,6 +148,8 @@ class FormBuilder implements FormBuilderInterface {
    *   The event dispatcher.
    * @param \Drupal\Core\Routing\UrlGeneratorInterface $url_generator
    *   The URL generator.
+   * @param \Drupal\Core\Config\ConfigFactory $config
+   *   The config factory service.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation_manager
    *   The translation manager.
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
@@ -142,11 +157,12 @@ class FormBuilder implements FormBuilderInterface {
    * @param \Drupal\Core\HttpKernel $http_kernel
    *   The HTTP kernel.
    */
-  public function __construct(ModuleHandlerInterface $module_handler, KeyValueExpirableFactory $key_value_expirable_factory, EventDispatcherInterface $event_dispatcher, UrlGeneratorInterface $url_generator, TranslationInterface $translation_manager, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
+  public function __construct(ModuleHandlerInterface $module_handler, KeyValueExpirableFactory $key_value_expirable_factory, EventDispatcherInterface $event_dispatcher, UrlGeneratorInterface $url_generator, ConfigFactory $config_factory, TranslationInterface $translation_manager, CsrfTokenGenerator $csrf_token = NULL, HttpKernel $http_kernel = NULL) {
     $this->moduleHandler = $module_handler;
     $this->keyValueExpirableFactory = $key_value_expirable_factory;
     $this->eventDispatcher = $event_dispatcher;
     $this->urlGenerator = $url_generator;
+    $this->configFactory = $config_factory;
     $this->translationManager = $translation_manager;
     $this->csrfToken = $csrf_token;
     $this->httpKernel = $http_kernel;
@@ -390,8 +406,10 @@ public function getCache($form_build_id, &$form_state) {
    * {@inheritdoc}
    */
   public function setCache($form_build_id, $form, $form_state) {
-    // 6 hours cache life time for forms should be plenty.
-    $expire = 21600;
+    // Form cache expiration should be at least the max age of the page itself
+    // to ensure AJAX functionality.
+    $page_max_age = $this->configFactory->get('system.performance')->get('cache.page.max_age');
+    $expire = max(self::MIN_CACHE_TTL, $page_max_age);
 
     // Cache form structure.
     if (isset($form)) {
diff --git a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
index 4e3c0db..222cf7d 100644
--- a/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
+++ b/core/tests/Drupal/Tests/Core/Form/FormBuilderTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Tests\Core\Form {
 
+use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Form\FormBuilder;
 use Drupal\Core\Form\FormInterface;
@@ -42,6 +43,13 @@ class FormBuilderTest extends UnitTestCase {
   protected $moduleHandler;
 
   /**
+   * The config factory.
+   *
+   * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
    * The expirable key value store used by form cache.
    *
    * @var \PHPUnit_Framework_MockObject_MockObject|\Drupal\Core\KeyValueStore\KeyValueStoreExpirableInterface
@@ -97,12 +105,15 @@ public function setUp() {
 
     $event_dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
     $this->urlGenerator = $this->getMock('Drupal\Core\Routing\UrlGeneratorInterface');
+    $this->configFactory = $this->getConfigFactoryStub(array(
+      'system.performance' => array('cache.page.max_age' => 43200),
+    ));
     $translation_manager = $this->getStringTranslationStub();
     $this->csrfToken = $this->getMockBuilder('Drupal\Core\Access\CsrfTokenGenerator')
       ->disableOriginalConstructor()
       ->getMock();
 
-    $this->formBuilder = new TestFormBuilder($this->moduleHandler, $key_value_expirable_factory, $event_dispatcher, $this->urlGenerator, $translation_manager, $this->csrfToken);
+    $this->formBuilder = new TestFormBuilder($this->moduleHandler, $key_value_expirable_factory, $event_dispatcher, $this->urlGenerator, $this->configFactory, $translation_manager, $this->csrfToken);
     $this->formBuilder->setRequest(new Request());
 
     $this->account = $this->getMock('Drupal\Core\Session\AccountInterface');
@@ -476,6 +487,133 @@ public function testGetCache() {
   }
 
   /**
+   * Tests the setCache() method for an authenticated user.
+   */
+  public function testSetCacheAsAuthenticated() {
+    $form = test_form_id();
+    $form_build_id = 'test_form_build_id';
+    $form_state = array('cacheable_key' => 'test');
+
+    // Will the current user to be authenticated.
+    $this->account->expects($this->exactly(1))
+      ->method('isAuthenticated')
+      ->will($this->returnValue(TRUE));
+
+    // Ensure the CSRF token is retrieved for authenticated users.
+    $csrf_token = 'csrf_token';
+    $this->csrfToken->expects($this->exactly(1))
+      ->method('get')
+      ->will($this->returnValue($csrf_token));
+
+    // Make sure the form is cached with the CSRF token included.
+    $this->formCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form + array('#cache_token' => $csrf_token), $this->isType('int'));
+    // Also ensure the form state is cached.
+    $this->formStateCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form_state, $this->isType('int'));
+
+    $this->formBuilder->setCache($form_build_id, $form, $form_state);
+  }
+
+  /**
+   * Tests the setCache() method for an unauthenticated user.
+   */
+  public function testSetCacheAsUnauthenticated() {
+    $form = test_form_id();
+    $form_build_id = 'test_form_build_id';
+    $form_state = array('cacheable_key' => 'test');
+
+    // Will the current user to be unauthenticated.
+    $this->account->expects($this->exactly(1))
+      ->method('isAuthenticated')
+      ->will($this->returnValue(FALSE));
+
+    // Ensure the CSRF token is never retrieved for unauthenticated users.
+    $this->csrfToken->expects($this->never())
+      ->method('get');
+
+    // Make sure the form and form state are cached.
+    $this->formCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form, $this->isType('int'));
+    $this->formStateCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form_state, $this->isType('int'));
+
+    $this->formBuilder->setCache($form_build_id, $form, $form_state);
+  }
+
+  /**
+   * Tests the setCache() method when page cache age is greater than form cache.
+   */
+  public function testSetCacheWhenPageCacheGreater() {
+    $form = test_form_id();
+    $form_build_id = 'test_form_build_id';
+    $form_state = array('cacheable_key' => 'test');
+
+    // Explicitly set our page cache max age greater than the form cache.
+    $builder_class = get_class($this->formBuilder);
+    $config_factory = $this->getConfigFactoryStub(array(
+      'system.performance' => array(
+        'cache.page.max_age' => $builder_class::MIN_CACHE_TTL + 120,
+      ),
+    ));
+    $this->formBuilder->setConfigFactory($config_factory);
+    $expected_expire = $config_factory->get('system.performance')->get('cache.page.max_age');
+
+    // Will the current user to be unauthenticated.
+    $this->account->expects($this->exactly(1))
+      ->method('isAuthenticated')
+      ->will($this->returnValue(FALSE));
+
+    // Make sure the form and form state are cached.
+    $this->formCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form, $expected_expire);
+    $this->formStateCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form_state, $expected_expire);
+
+    $this->formBuilder->setCache($form_build_id, $form, $form_state);
+  }
+
+  /**
+   * Tests the setCache() method when page cache age is less than form cache.
+   */
+  public function testSetCacheWhenPageCacheLesser() {
+    $form = test_form_id();
+    $form_build_id = 'test_form_build_id';
+    $form_state = array('cacheable_key' => 'test');
+
+    // Explicitly set our page cache max age less than the form cache.
+    $builder_class = get_class($this->formBuilder);
+    $config_factory = $this->getConfigFactoryStub(array(
+      'system.performance' => array(
+        'cache.page.max_age' => $builder_class::MIN_CACHE_TTL - 120,
+      ),
+    ));
+    $this->formBuilder->setConfigFactory($config_factory);
+    $expected_expire = $builder_class::MIN_CACHE_TTL;
+
+    // Will the current user to be unauthenticated.
+    $this->account->expects($this->exactly(1))
+      ->method('isAuthenticated')
+      ->will($this->returnValue(FALSE));
+
+    // Make sure the form and form state are cached.
+    $this->formCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form, $expected_expire);
+    $this->formStateCache->expects($this->exactly(1))
+      ->method('setWithExpire')
+      ->with($form_build_id, $form_state, $expected_expire);
+
+    $this->formBuilder->setCache($form_build_id, $form, $form_state);
+  }
+
+  /**
    * Asserts that the expected form structure is found in a form for a given key.
    *
    * @param array $expected_form
@@ -507,6 +645,13 @@ public function setCurrentUser(AccountInterface $account) {
   }
 
   /**
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   */
+  public function setConfigFactory(ConfigFactory $config_factory) {
+    $this->configFactory = $config_factory;
+  }
+
+  /**
    * {@inheritdoc}
    */
   protected function getElementInfo($type) {
