diff --git a/redirect.install b/redirect.install
index fa67aaa..e5bbc78 100644
--- a/redirect.install
+++ b/redirect.install
@@ -356,3 +356,28 @@ function _redirect_migrate_path_redirect_variables() {
     variable_del($old_variable);
   }
 }
+
+/**
+ * Remove existing redirect causing infinite loops.
+ */
+function redirect_update_7100(&$sandbox) {
+  $query = db_select('redirect', 'r');
+  $query->join(
+    'url_alias',
+    'u',
+    'r.source = u.alias AND r.redirect = u.source AND r.language = u.language'
+  );
+  $query->fields('r', array('rid'));
+  $result = $query->execute();
+  $rid_to_delete = array();
+  while ($record = $result->fetchAssoc()) {
+    $rid_to_delete[] = $record['rid'];
+  }
+  if (count($rid_to_delete) > 0) {
+    $deleted = db_delete('redirect')->condition('rid', $rid_to_delete, 'IN')->execute();
+    return format_plural($deleted, '1 circular redirect causing infinite loop was deleted.', '@count circular redirects causing infinite loop were deleted.');
+  }
+  else {
+    return t('No circular redirect causing infinite loop was found.');
+  }
+}
diff --git a/redirect.module b/redirect.module
index 9f7a256..e341fac 100644
--- a/redirect.module
+++ b/redirect.module
@@ -394,6 +394,8 @@ function redirect_path_update(array $path) {
   }
 
   if (!empty($path['original']['pid']) && $path['original']['pid'] == $path['pid'] && $path['original']['alias'] != $path['alias']) {
+    // Delete all redirects having the same source as this alias.
+    redirect_delete_by_path($path['alias'], $path['language'], FALSE);
     $redirect = new stdClass();
     redirect_object_prepare($redirect);
     $redirect->source = $path['original']['alias'];
@@ -408,6 +410,16 @@ function redirect_path_update(array $path) {
 }
 
 /**
+ * Implements hook_path_insert().
+ */
+function redirect_path_insert(array $path) {
+  if (!empty($path['alias'])) {
+    // Delete all redirects having the same source as this alias.
+    redirect_delete_by_path($path['alias'], $path['language'], FALSE);
+  }
+}
+
+/**
  * Implements hook_path_delete().
  */
 function redirect_path_delete($path) {
@@ -849,15 +861,20 @@ function redirect_delete($rid) {
  *
  * @ingroup redirect_api
  */
-function redirect_delete_by_path($path) {
+function redirect_delete_by_path($path, $language = FALSE, $match_subpaths_and_redirect = TRUE) {
   $query = db_select('redirect');
   $query->addField('redirect', 'rid');
   $query_or = db_or();
   $query_or->condition('source', db_like($path), 'LIKE');
-  $query_or->condition('source', db_like($path . '/') . '%', 'LIKE');
-  $query_or->condition('redirect', db_like($path), 'LIKE');
-  $query_or->condition('redirect', db_like($path . '/') . '%', 'LIKE');
+  if ($match_subpaths_and_redirect) {
+    $query_or->condition('source', db_like($path . '/') . '%', 'LIKE');
+    $query_or->condition('redirect', db_like($path), 'LIKE');
+    $query_or->condition('redirect', db_like($path . '/') . '%', 'LIKE');
+  }
   $query->condition($query_or);
+  if ($language) {
+    $query->condition('language', $language);
+  }
   $rids = $query->execute()->fetchCol();
 
   if ($rids) {
@@ -1017,6 +1034,10 @@ function redirect_redirect($redirect = NULL) {
 
   // Continue if the redirect has not been disabled by hook_redirect_alter().
   if (isset($redirect->redirect) && isset($redirect->callback) && $redirect->redirect !== FALSE && function_exists($redirect->callback)) {
+    // Prevent circular redirects.
+    if ($GLOBALS['base_root'] . request_uri() == url($redirect->redirect, array('absolute' => TRUE) + $redirect->redirect_options)) {
+      return FALSE;
+    }
     // Perform the actual redirect.
     $callback = $redirect->callback;
     $callback($redirect);
diff --git a/redirect.test b/redirect.test
index 1c237f8..546a7d1 100644
--- a/redirect.test
+++ b/redirect.test
@@ -234,10 +234,14 @@ class RedirectFunctionalTest extends RedirectTestHelper {
     //$this->assertRedirect($redirect);
 
     $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'first-alias'), 'Save');
+    $this->assertResponse(200,"Changing node's alias back to 'first-alias' does not break page load with a circular redirect.");
+    $this->assertNoText('Infinite redirect loop prevented.');
     //$redirect = redirect_load_by_source('second-alias');
     //$this->assertRedirect($redirect);
 
     $this->drupalPost("node/{$node->nid}/edit", array('path[alias]' => 'second-alias'), 'Save');
+    $this->assertResponse(200,"Changing node's alias back to 'second-alias' does not break page load with a circular redirect.");
+    $this->assertNoText('Infinite redirect loop prevented.');
     //$redirect = redirect_load_by_source('first-alias');
     //$this->assertRedirect($redirect);
   }
