diff --git a/includes/webform.pages.inc b/includes/webform.pages.inc
index def36342..a5690a59 100644
--- a/includes/webform.pages.inc
+++ b/includes/webform.pages.inc
@@ -336,7 +336,7 @@ function webform_configure_form($form, &$form_state, $node) {
     '#type' => 'checkbox',
     '#title' => t('Show "Save draft" button'),
     '#default_value' => $node->webform['allow_draft'],
-    '#description' => t('Allow your users to save and finish the form later. This option is available only for authenticated users.'),
+    '#description' => t('Allow your users to save and finish the form later. This option may impact performance because it may cause a large number of sessions to bypass caching, especially for multipage forms.'),
   );
   $form['advanced']['auto_save'] = array(
     '#type' => 'checkbox',
diff --git a/tests/WebformTestCase.test b/tests/WebformTestCase.test
index afb10deb..e813310d 100644
--- a/tests/WebformTestCase.test
+++ b/tests/WebformTestCase.test
@@ -1021,6 +1021,7 @@ class WebformTestCase extends DrupalWebTestCase {
       'format' => '1',
       'webform' => array(
         'confirmation' => 'Thanks!',
+        'allow_draft' => 1,
       ) + webform_node_defaults(),
     );
 
diff --git a/webform.install b/webform.install
index f8819092..626fe798 100644
--- a/webform.install
+++ b/webform.install
@@ -590,6 +590,13 @@ function webform_schema() {
         'type' => 'varchar',
         'length' => 128,
       ),
+      'session_id' => array(
+        'description' => 'For anonymous submissions, the drupal session id.',
+        'type' => 'varchar',
+        'length' => '128',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
     ),
     'primary key' => array('sid'),
     'unique keys' => array(
@@ -783,6 +790,7 @@ function webform_install() {
  */
 function webform_uninstall() {
   // Unset webform variables.
+  variable_del('webform_draft_found_message');
   variable_del('webform_blocks');
   variable_del('webform_tracking_mode');
   variable_del('webform_allowed_tags');
@@ -2351,3 +2359,22 @@ function webform_update_7430() {
 
   return t('Webform emails may now be disabled.');
 }
+
+/**
+ * Add a column to the submission table to store the session id of anonymous
+ * submissions. Sites with many, many submissions may wish to execute this
+ * update with 'drush updatedb'.
+ */
+function webform_update_7431() {
+  // Add session_id column to webform_submissions.
+  if (!db_field_exists('webform_submissions', 'session_id')) {
+    $spec = array(
+      'description' => 'For anonymous submissions, the drupal session id.',
+      'type' => 'varchar',
+      'length' => '128',
+      'not null' => TRUE,
+      'default' => 0,
+    );
+    db_add_field('webform_submissions', 'session_id', $spec);
+  }
+}
diff --git a/webform.module b/webform.module
index bb33f855..2c6df4c9 100644
--- a/webform.module
+++ b/webform.module
@@ -769,6 +769,75 @@ function webform_permission() {
 }
 
 /**
+ * Implements hook_cron().
+ */
+function webform_cron() {
+  // Determine if HTTPS is activated, if so use the {sessions} column ssid instead sid
+  $session_column = 'sid';
+  if ((isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') || $_SERVER['HTTPS'] == 'on') {
+    $session_column = 'ssid';
+  }
+
+  // Query for anonymous draft webform submissions with 1) without associated
+  // sessions or 2) with expired sessions. Expired sessions may or may not exist
+  // depending on how often PHP garbage collection is configured to remove
+  // sessions.
+  $query = db_select('webform_submissions', 'ws');
+  $query->fields('ws', array('nid', 'sid'));
+  $query->condition('ws.uid', 0, '=');
+  $query->condition('ws.is_draft', 1, '=');
+  $query->leftJoin('sessions', 's', 's.' . $session_column . ' = ws.session_id');
+
+  // Query for webform submissions without matching session records or with
+  // expired sessions.
+  $db_or = db_or();
+  $db_or->isNull('s.' . $session_column);
+  $cookie_info = session_get_cookie_params();
+  $db_or->condition('s.timestamp', REQUEST_TIME + $cookie_info['lifetime'], '<');
+  $query->condition($db_or);
+
+  $result = $query->execute();
+  $count = $result->rowCount();
+
+  $queue = DrupalQueue::get('webform_delete_submission');
+  foreach ($result as $row) {
+    // Queue the submission for deletion.
+    $queue->createItem($row);
+  }
+  if ($count > 0) {
+    watchdog('webform', 'Queued !count stale anonymous drafts.', array('!count' => $count), WATCHDOG_INFO);
+  }
+}
+
+/**
+ * Implements hook_cron_queue_info().
+ */
+function webform_cron_queue_info() {
+  $queues['webform_delete_submission'] = array(
+    'worker callback' => 'webform_delete_submission_run',
+    'time' => 60,
+  );
+  return $queues;
+}
+
+/**
+ * Worker callback for the delete_submission queue.
+ *
+ * @param object $item
+ *   Queue data with Node ID and Submission ID properties.
+ */
+function webform_delete_submission_run(stdClass $item) {
+  module_load_include('inc', 'webform', 'includes/webform.submissions');
+
+  $node = node_load($item->nid);
+  $submission = webform_get_submission($item->nid, $item->sid);
+  if ($node) {
+    webform_submission_delete($node, $submission);
+    watchdog('webform', 'Deleted a stale anonymous draft.', array(), WATCHDOG_INFO);
+  }
+}
+
+/**
  * Implements hook_theme().
  */
 function webform_theme() {
@@ -2039,13 +2108,40 @@ function webform_node_view($node, $view_mode) {
 
   // Check if this user has a draft for this webform.
   $resume_draft = FALSE;
-  if (($node->webform['allow_draft'] || $node->webform['auto_save']) && $user->uid != 0) {
+  $draft_sid = FALSE;
+  if (($node->webform['allow_draft'] || $node->webform['auto_save'])) {
     // Draft found - display form with draft data for further editing.
-    if ($draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid)) {
+    if ($user->uid != 0) {
+      $draft_sid = _webform_fetch_draft_sid($node->nid, $user->uid);
+    }
+    // On first form load for anonymous user, _SESSION has not been set.
+    if (!$draft_sid && isset($_SESSION['webform_submission'])) {
+      // To use legacy key=value order as [sid]=nid.
+      $tmp_s = array_flip($_SESSION['webform_submission']);
+      // Make sure the draft is for the current form.
+      $draft_sid = (isset($tmp_s[$node->nid])) ? $tmp_s[$node->nid] : 0;
+    }
+    if ($draft_sid) {
       module_load_include('inc', 'webform', 'includes/webform.submissions');
       $submission = webform_get_submission($node->nid, $draft_sid);
-      $enabled = TRUE;
-      $resume_draft = TRUE;
+      if ($submission && $submission->is_draft) {
+        $enabled = TRUE;
+        $resume_draft = TRUE;
+        // If user created draft as anonymous and is now logged in, change the
+        // user ID for this submission.
+        if ($user->uid != 0 && $submission->uid != $user->uid) {
+          $submission->uid = $user->uid;
+          $submission->session_id = session_id();
+          // Save the submission now, so that the user ID is saved even if they
+          // don't press 'save as draft' again.
+          webform_submission_update($node, $submission);
+        }
+      }
+      else {
+        // No draft or submission already closed
+        $draft_sid = FALSE;
+        $submission = FALSE;
+      }
     }
   }
 
@@ -2566,7 +2662,7 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r
       // a new form. This applies to the node view page, but not to a submission
       // edit page (where they presummably know what submission they are
       // editing).
-      if ($resume_draft && empty($form_state['input'])) {
+      if ($resume_draft && empty($form_state['input']) && webform_variable_get('webform_draft_found_message')) {
         drupal_set_message(t('A partially-completed form was found. Please complete the remaining portions.'));
       }
     }
@@ -2728,7 +2824,7 @@ function webform_client_form($form, &$form_state, $node, $submission = FALSE, $r
     );
 
     // Add the draft button.
-    if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft) && $user->uid != 0) {
+    if ($node->webform['allow_draft'] && (empty($submission) || $submission->is_draft)) {
       $form['actions']['draft'] = array(
         '#type' => 'submit',
         '#value' => t('Save Draft'),
@@ -3313,7 +3409,7 @@ function webform_client_form_pages($form, &$form_state) {
   }
 
   // Inform the submit handlers that a draft will be saved.
-  $form_state['save_draft'] = in_array($form_state['values']['op'], array($draft_op, '__AUTOSAVE__')) || ($node->webform['auto_save'] && !$form_state['values']['details']['finished'] && !$form_state['webform_completed'] && user_is_logged_in());
+  $form_state['save_draft'] = in_array($form_state['values']['op'], array($draft_op, '__AUTOSAVE__')) || ($node->webform['auto_save'] && !$form_state['values']['details']['finished'] && !$form_state['webform_completed']);
 
   // Determine what we need to do on the next page.
   if (!empty($form_state['save_draft']) || !$form_state['webform_completed']) {
@@ -3379,6 +3475,11 @@ function webform_client_form_submit($form, &$form_state) {
     $submission->highest_valid_page = end($form_state['clicked_button']['#parents']) == 'next' && $form_state['values']['op'] != '__AUTOSAVE__'
       ? $form_state['webform']['page_num']
       : $form_state['webform']['page_num'] - 1;
+
+    // If they're anonymous, save the session ID so we can tidy up later.
+    if (!$user->uid) {
+      $submission->session_id = session_id();
+    }
   }
 
   // If there is no data to be saved (such as on a multipage form with no fields
@@ -3411,7 +3512,7 @@ function webform_client_form_submit($form, &$form_state) {
 
     // Save session information about this submission for anonymous users,
     // allowing them to access or edit their submissions.
-    if (!$user->uid && user_access('access own webform submissions')) {
+    if (!$user->uid) {
       $_SESSION['webform_submission'][$sid] = $node->nid;
     }
   }
@@ -3441,40 +3542,46 @@ function webform_client_form_submit($form, &$form_state) {
     // No redirect needed. No confirmation message needed.
     $message = FALSE;
   }
-  elseif (!empty($form_state['values']['details']['finished'])) {
-    $message = t('Submission updated.');
-    $redirect = "node/{$node->nid}/submission/$sid";
-  }
-  elseif (!empty($node->webform['confirmation_block'])) {
-    $message = FALSE;
-    // Webform was submitted in a block and the confirmation message is to be
-    // displayed in the block.
-    $_SESSION['webform_confirmation'][$node->nid] = array(
-      'sid' => $sid,
-      'confirmation_page' => $redirect_url == '<confirmation>',
-    );
-    drupal_page_is_cacheable(FALSE);
-  }
-  elseif ($redirect_url == '<none>') {
-    // No redirect needed. Show a confirmatin message if there is one.
-  }
-  elseif ($redirect_url == '<confirmation>') {
-    // No confirmation message needed because it will be shown on the
-    // confirmation page.
-    $message = FALSE;
-    $query = array('sid' => $sid);
-    if ((int) $user->uid === 0) {
-      $query['token'] = webform_get_submission_access_token($submission);
-    }
-    $redirect = array('node/' . $node->nid . '/done', array('query' => $query));
-  }
   else {
-    // Clean up the redirect URL, filter it for tokens and detect external
-    // domains. If the redirect is to an external URL, then don't show the
-    // confirmation message.
-    $redirect = webform_replace_url_tokens($redirect_url, $node, $submission);
-    if ($redirect[1]['#webform_external']) {
+    // Clear _SESSION for anonymous user after submission.
+    if (isset($_SESSION['webform_submission'][$sid])) {
+      unset($_SESSION['webform_submission'][$sid]);
+    }
+    if (!empty($form_state['values']['details']['finished'])) {
+      $message = t('Submission updated.');
+      $redirect = "node/{$node->nid}/submission/$sid";
+    }
+    elseif (!empty($node->webform['confirmation_block'])) {
+      $message = FALSE;
+      // Webform was submitted in a block and the confirmation message is to be
+      // displayed in the block.
+      $_SESSION['webform_confirmation'][$node->nid] = array(
+        'sid' => $sid,
+        'confirmation_page' => $redirect_url == '<confirmation>',
+      );
+      drupal_page_is_cacheable(FALSE);
+    }
+    elseif ($redirect_url == '<none>') {
+      // No redirect needed. Show a confirmation message if there is one.
+    }
+    elseif ($redirect_url == '<confirmation>') {
+      // No confirmation message needed because it will be shown on the
+      // confirmation page.
       $message = FALSE;
+      $query = array('sid' => $sid);
+      if ((int) $user->uid === 0) {
+        $query['token'] = webform_get_submission_access_token($submission);
+      }
+      $redirect = array('node/' . $node->nid . '/done', array('query' => $query));
+    }
+    else {
+      // Clean up the redirect URL, filter it for tokens and detect external
+      // domains. If the redirect is to an external URL, then don't show the
+      // confirmation message.
+      $redirect = webform_replace_url_tokens($redirect_url, $node, $submission);
+      if ($redirect[1]['#webform_external']) {
+        $message = FALSE;
+      }
     }
   }
 
@@ -4282,6 +4389,10 @@ function webform_page_labels($node, $form_state = array()) {
  */
 function webform_variable_get($variable) {
   switch ($variable) {
+    case 'webform_draft_found_message':
+      $result = variable_get('webform_draft_found_message',FALSE);
+      break;
+
     case 'webform_blocks':
       $result = variable_get('webform_blocks', array());
       break;
