diff --git a/includes/form.inc b/includes/form.inc
index e749239ef9..3b2032eb72 100644
--- a/includes/form.inc
+++ b/includes/form.inc
@@ -555,8 +555,10 @@ function form_get_cache($form_build_id, &$form_state) {
  * Stores a form in the cache.
  */
 function form_set_cache($form_build_id, $form, $form_state) {
-  // 6 hours cache life time for forms should be plenty.
-  $expire = 21600;
+  // The default cache_form expiration is 6 hours. On busy sites, the cache_form
+  // table can become very large. A shorter cache lifetime can help to keep the
+  // table's size under control.
+  $expire = variable_get('form_cache_expiration', 21600);
 
   // Ensure that the form build_id embedded in the form structure is the same as
   // the one passed in as a parameter. This is an additional safety measure to
diff --git a/modules/simpletest/tests/form.test b/modules/simpletest/tests/form.test
index a441ceeba4..4eea8ea104 100644
--- a/modules/simpletest/tests/form.test
+++ b/modules/simpletest/tests/form.test
@@ -1421,6 +1421,59 @@ class FormsFormStoragePageCacheTestCase extends DrupalWebTestCase {
 }
 
 /**
+ * Test cache_form.
+ */
+class FormsFormCacheTestCase extends DrupalWebTestCase {
+  public static function getInfo() {
+    return array(
+        'name' => 'Form caching',
+        'description' => 'Tests storage and retrieval of forms from cache.',
+        'group' => 'Form API',
+    );
+  }
+
+  function setUp() {
+    parent::setUp('form_test');
+  }
+
+  /**
+   * Tests storing and retrieving the form from cache.
+   */
+  function testCacheForm() {
+    $form = drupal_get_form('form_test_cache_form');
+    $form_state = array('foo' => 'bar', 'build_info' => array('baz'));
+    form_set_cache($form['#build_id'], $form, $form_state);
+
+    $cached_form_state = array();
+    $cached_form = form_get_cache($form['#build_id'], $cached_form_state);
+
+    $this->assertEqual($cached_form['#build_id'], $form['#build_id'], 'Form retrieved from cache_form successfully.');
+    $this->assertEqual($cached_form_state['foo'], 'bar', 'Data retrieved from cache_form successfully.');
+  }
+
+  /**
+   * Tests changing form_cache_expiration.
+   */
+  function testCacheFormCustomExpiration() {
+    variable_set('form_cache_expiration', -1 * (24 * 60 * 60));
+
+    $form = drupal_get_form('form_test_cache_form');
+    $form_state = array('foo' => 'bar', 'build_info' => array('baz'));
+    form_set_cache($form['#build_id'], $form, $form_state);
+
+    // Clear expired entries from cache_form, which should include the entry we
+    // just stored. Without this, the form will still be retrieved from cache.
+    cache_clear_all(NULL, 'cache_form');
+
+    $cached_form_state = array();
+    $cached_form = form_get_cache($form['#build_id'], $cached_form_state);
+
+    $this->assertNull($cached_form, 'Expired form was not returned from cache.');
+    $this->assertTrue(empty($cached_form_state), 'No data retrieved from cache for expired form.');
+  }
+}
+
+/**
  * Test wrapper form callbacks.
  */
 class FormsFormWrapperTestCase extends DrupalWebTestCase {
diff --git a/modules/simpletest/tests/form_test.module b/modules/simpletest/tests/form_test.module
index 097e58841d..913602d24d 100644
--- a/modules/simpletest/tests/form_test.module
+++ b/modules/simpletest/tests/form_test.module
@@ -919,6 +919,24 @@ function form_test_storage_page_cache_rebuild($form, &$form_state) {
 }
 
 /**
+ * A simple form for testing form caching.
+ */
+function form_test_cache_form($form, &$form_state) {
+  $form['title'] = array(
+      '#type' => 'textfield',
+      '#title' => 'Title',
+      '#required' => TRUE,
+  );
+
+  $form['submit'] = array(
+      '#type' => 'submit',
+      '#value' => 'Save',
+  );
+
+  return $form;
+}
+
+/**
  * A form for testing form labels and required marks.
  */
 function form_label_test_form() {
diff --git a/sites/default/default.settings.php b/sites/default/default.settings.php
index b044be3bc6..a152b287e9 100644
--- a/sites/default/default.settings.php
+++ b/sites/default/default.settings.php
@@ -479,6 +479,23 @@ ini_set('session.cookie_lifetime', 2000000);
 # $conf['block_cache_bypass_node_grants'] = TRUE;
 
 /**
+ * Expiration of cache_form entries:
+ *
+ * Drupal's Form API stores details of forms in cache_form and these entries are
+ * kept for at least 6 hours by default. Expired entries are cleared by cron.
+ * Busy sites can encounter problems with the cache_form table becoming very
+ * large. It's possible to mitigate this by setting a shorter expiration for
+ * cached forms. In some cases it may be desirable to set a longer cache
+ * expiration, for example to prolong cache_form entries for Ajax forms in
+ * cached HTML.
+ *
+ * @see form_set_cache()
+ * @see system_cron()
+ * @see ajax_get_form()
+ */
+# $conf['form_cache_expiration'] = 21600;
+
+/**
  * String overrides:
  *
  * To override specific strings on your site with or without enabling the Locale
