diff --git includes/common.inc includes/common.inc
index 0bae037..5c1c243 100644
--- includes/common.inc
+++ includes/common.inc
@@ -625,42 +625,73 @@ function drupal_encode_path($path) {
  *
  * Usually the redirected URL is constructed from this function's input
  * parameters. However you may override that behavior by setting a
- * destination in either the $_REQUEST-array (i.e. by using
- * the query string of an URI) This is used to direct the user back to
- * the proper page after completing a form. For example, after editing
- * a post on the 'admin/content'-page or after having logged on using the
- * 'user login'-block in a sidebar. The function drupal_get_destination()
+ * destination in either the $_REQUEST-array (i.e., by using
+ * the query string of an URI). This is used to direct the user back to
+ * the proper page after completing a form; for example, after editing
+ * a post on the admin/content page, or after having logged on using the
+ * user login block in a sidebar. The function drupal_get_destination()
  * can be used to help set the destination URL.
  *
  * Drupal will ensure that messages set by drupal_set_message() and other
  * session data are written to the database before the user is redirected.
  *
- * This function ends the request; use it instead of a return in your menu
- * callback.
+ * This function ends the page request immediately by default. If the page
+ * request cannot be safely terminated, use the delay option.
  *
  * @param $path
- *   A Drupal path or a full URL.
+ *   A Drupal path or a full URL to redirect to. Ignored if $_GET['destination']
+ *   is supplied and it's an internal path, unless $options['skip_destination']
+ *   is set.
  * @param $options
- *   An associative array of additional URL options to pass to url().
+ *   An associative array of options. The options are passed to url() to
+ *   construct the URL to redirect to (see url() documentation for details).
+ *   Additional options:
+ *   - delay: If TRUE, this redirect is delayed and will be applied just before
+ *     the current page request is delivered. Use this option in situations
+ *     where an immediate redirect could cause problems, such as when a save
+ *     operation is interrupted. If multiple delayed calls to drupal_goto()
+ *     occur during one page request, the first one takes precedence.
+ *   - skip_destination: If TRUE, ignore paths supplied in $_GET['destination'].
  * @param $http_response_code
- *   Valid values for an actual "goto" as per RFC 2616 section 10.3 are:
- *   - 301 Moved Permanently (the recommended value for most redirects)
- *   - 302 Found (default in Drupal and PHP, sometimes used for spamming search
- *         engines)
- *   - 303 See Other
- *   - 304 Not Modified
- *   - 305 Use Proxy
- *   - 307 Temporary Redirect (alternative to "503 Site Down for Maintenance")
+ *   The HTTP response code to send with the redirect. Valid values for an
+ *   actual "goto" as per RFC 2616 section 10.3 are:
+ *   - 301: Moved Permanently (the recommended value for most redirects).
+ *   - 302: Found (default in Drupal and PHP, sometimes used for spamming search
+ *      engines).
+ *   - 303: See Other.
+ *   - 304: Not Modified.
+ *   - 305: Use Proxy.
+ *   - 307: Temporary Redirect (alternative to "503 Site Down for Maintenance").
  *   Note: Other values are defined by RFC 2616, but are rarely used and poorly
  *   supported.
  *
  * @see drupal_get_destination()
  * @see url()
+ * @see drupal_deliver_page()
  */
 function drupal_goto($path = '', array $options = array(), $http_response_code = 302) {
-  // A destination in $_GET always overrides the function arguments.
-  // We do not allow absolute URLs to be passed via $_GET, as this can be an attack vector.
-  if (isset($_GET['destination']) && !url_is_external($_GET['destination'])) {
+  $delayed_redirect = &drupal_static('drupal_goto_delayed');
+
+  if (!empty($options['delay'])) {
+    // Record a delayed redirect only in case there is none yet.
+    if (!$delayed_redirect) {
+      $delayed_redirect = array(
+        'path' => $path,
+        'options' => $options,
+        'http_response_code' => $http_response_code,
+      );
+    }
+    return;
+  }
+  // If a delayed redirect was previously saved, extract the saved parameters.
+  if ($delayed_redirect) {
+    extract($delayed_redirect);
+  }
+
+  // A destination in $_GET overrides the function arguments unless the
+  // skip_destination option has been enabled. We do not allow absolute URLs
+  // to be passed via $_GET, as this can be an attack vector.
+  if (isset($_GET['destination']) && !url_is_external($_GET['destination']) && empty($options['skip_destination'])) {
     $destination = drupal_parse_url($_GET['destination']);
     $path = $destination['path'];
     $options['query'] = $destination['query'];
@@ -2149,7 +2180,7 @@ function url($path = NULL, array $options = array()) {
  * @param $path
  *   The internal path or external URL being linked to, such as "node/34" or
  *   "http://example.com/foo".
- * @return 
+ * @return
  *   Boolean TRUE or FALSE, where TRUE indicates an external path.
  */
 function url_is_external($path) {
@@ -2348,6 +2379,13 @@ function l($text, $path, array $options = array()) {
  * @see hook_page_delivery_callback_alter()
  */
 function drupal_deliver_page($page_callback_result, $default_delivery_callback = NULL) {
+  // If a delayed drupal_goto() was requested at some point in page processing,
+  // process by calling drupal_goto(), which will find the stored request and
+  // process it.
+  if (drupal_static('drupal_goto_delayed')) {
+    drupal_goto();
+  }
+
   if (!isset($default_delivery_callback) && ($router_item = menu_get_item())) {
     $default_delivery_callback = $router_item['delivery_callback'];
   }
@@ -6695,7 +6733,7 @@ function entity_form_field_validate($entity_type, $form, &$form_state) {
  * For some entity forms (e.g., forms with complex non-field data and forms that
  * simultaneously edit multiple entities), this behavior may be inappropriate,
  * so the #builder_function for such forms needs to implement the required
- * functionality instead of calling this function. 
+ * functionality instead of calling this function.
  */
 function entity_form_submit_build_entity($entity_type, $entity, $form, &$form_state) {
   $info = entity_get_info($entity_type);
diff --git modules/system/system.module modules/system/system.module
index c963cb4..05c272e 100644
--- modules/system/system.module
+++ modules/system/system.module
@@ -3069,18 +3069,24 @@ function system_goto_action_submit($form, $form_state) {
 /**
  * Redirects to a different URL.
  *
+ * The redirect does not happen immediately, but rather at the end of the page
+ * request, to allow processing to complete correctly (saving data, invoking
+ * hooks, etc.).
+ *
  * @param $entity
  *   Ignored.
  * @param array $context
  *   Array with the following elements:
- *   - 'url': URL to redirect to. This will be passed through
- *     token_replace().
+ *   - url: URL to redirect to. This will be passed through token_replace().
  *   - Other elements will be used as the data for token replacement.
  *
  * @ingroup actions
  */
 function system_goto_action($entity, $context) {
-  drupal_goto(token_replace($context['url'], $context));
+  // Do not redirect during batch processing.
+  if (arg(0) != 'batch') {
+    drupal_goto(token_replace($context['url'], $context), array('delay' => TRUE, 'skip_destination' => TRUE));
+  }
 }
 
 /**
diff --git modules/trigger/tests/trigger_test.module modules/trigger/tests/trigger_test.module
index d19f8dc..6e016af 100644
--- modules/trigger/tests/trigger_test.module
+++ modules/trigger/tests/trigger_test.module
@@ -132,3 +132,12 @@ function trigger_test_generic_any_action($context) {
   // Indicate successful execution by setting a persistent variable.
   variable_set('trigger_test_generic_any_action', variable_get('trigger_test_generic_any_action', 0) + 1);
 }
+
+/**
+ * Implements hook_node_insert() so we can make sure it's being invoked.
+ */
+function trigger_test_node_insert($node) {
+  $nodes = variable_get('trigger_test_nodes_inserted', array());
+  $nodes[] = $node->nid;
+  variable_set('trigger_test_nodes_inserted', $nodes);
+}
diff --git modules/trigger/trigger.test modules/trigger/trigger.test
index 0188e7d..098920a 100644
--- modules/trigger/trigger.test
+++ modules/trigger/trigger.test
@@ -132,6 +132,50 @@ class TriggerContentTestCase extends TriggerWebTestCase {
   }
 
   /**
+   * Tests that the go to page action doesn't break node creation.
+   *
+   * Verifies that if you add a trigger/action to redirect to the
+   * site home page when new content is saved, hook_node_insert() is still
+   * being invoked.
+   *
+   * See issue http://drupal.org/node/732542
+   */
+  function testGoToOnNodeSave() {
+    // Create a user who can create content and administer actions/triggers.
+    $web_user = $this->drupalCreateUser(array('administer nodes', 'administer actions', 'create page content'));
+    $this->drupalLogin($web_user);
+
+    // Create an advanced action to redirect to the site home page.
+    $edit = array(
+      'actions_label' => $this->randomName(),
+      'url' => '<front>',
+    );
+    $aid = $this->configureAdvancedAction('system_goto_action', $edit);
+
+    // Set up to trigger this advanced action when new content is saved.
+    $info = array(
+      'aid' => drupal_hash_base64($aid),
+    );
+    $this->drupalPost('admin/structure/trigger/node', $info, t('Assign'), array(), array(), 'trigger-node-insert-assign-form');
+
+    // Use the node add form to save content.
+    $before = variable_get('trigger_test_nodes_inserted', array());
+    $edit = array(
+      'title' => $this->randomName(),
+      'body[und][0][value]' => $this->randomName(),
+    );
+    $this->drupalPost('node/add/page', $edit, t('Save'));
+
+    // Verify that we are redirected to the home page.
+    $url = $this->getURL();
+    $this->assertEqual($url, url('<front>', array('absolute' => TRUE)), 'Node save was redirected to home page');
+
+    // Verify that hook_node_insert() was invoked.
+    $after = variable_get('trigger_test_nodes_inserted', array());
+    $this->assertTrue(count($after) > count($before), 'hook_node_insert() was invoked');
+  }
+
+  /**
    * Returns some info about each of the content actions.
    *
    * This is helper function for testActionsContent().
@@ -567,7 +611,7 @@ class TriggerOtherTestCase extends TriggerWebTestCase {
     $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), 'trigger-user-insert-assign-form');
 
     // Set action variable to FALSE.
-    variable_set( $action_id, FALSE );
+    variable_set($action_id, FALSE);
 
     // Create an unblocked user
     $web_user = $this->drupalCreateUser(array('administer users'));
@@ -600,15 +644,36 @@ class TriggerOtherTestCase extends TriggerWebTestCase {
     $edit = array('aid' => drupal_hash_base64($aid));
     $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), 'trigger-user-login-assign-form');
 
+    // Assign a configurable action 'Redirect to URL' to the user_login trigger.
+    $action_url_edit = array(
+      'actions_label' => $this->randomName(16),
+      'url' => 'contact',
+    );
+    $aid_url = $this->configureAdvancedAction('system_goto_action', $action_url_edit);
+    $edit = array('aid' => drupal_hash_base64($aid_url));
+    $this->drupalPost('admin/structure/trigger/user', $edit, t('Assign'), array(), array(), 'trigger-user-login-assign-form');
+
     // Verify that the action has been assigned to the correct hook.
     $actions = trigger_get_assigned_actions('user_login');
-    $this->assertEqual(1, count($actions), t('One Action assigned to the hook'));
+    $this->assertEqual(2, count($actions), t('Two Actions assigned to the hook'));
     $this->assertEqual($actions[$aid]['label'], $action_edit['actions_label'], t('Correct action label found.'));
+    $this->assertEqual($actions[$aid_url]['label'], $action_url_edit['actions_label'], t('Correct action label found.'));
 
     // User should get the configured message at login.
     $contact_user = $this->drupalCreateUser(array('access site-wide contact form'));;
-    $this->drupalLogin($contact_user);
+    $contact_user = $this->drupalCreateUser(array('access site-wide contact form'));
+    $this->drupalLogout();
+
+    // Trying to login from different page to be redirected.
+    $edit = array(
+      'name' => $contact_user->name,
+      'pass' => $contact_user->pass_raw,
+    );
+    $this->drupalPost('filter/tips', $edit, t('Log in'));
     $this->assertText($action_edit['message']);
+
+    // Verify that redirect happened.
+    $this->assertEqual(url('contact', array('absolute' => TRUE)), $this->url, t('Redirected successfully by the trigger.'));
   }
 
   /**
