diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 7375bd1..a97473b 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -40,6 +40,8 @@ typedef struct {
 	SoupAuth          *auth, *proxy_auth;
 
 	GSList            *disabled_features;
+
+	guint             redirection_count;
 } SoupMessagePrivate;
 #define SOUP_MESSAGE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_MESSAGE, SoupMessagePrivate))
 
@@ -95,6 +97,9 @@ void                soup_message_io_pause       (SoupMessage          *msg);
 void                soup_message_io_unpause     (SoupMessage          *msg);
 gboolean            soup_message_io_in_progress (SoupMessage          *msg);
 
+void           soup_message_increment_redirection_count (SoupMessage *msg);
+guint          soup_message_get_redirection_count       (SoupMessage *msg);
+
 gboolean soup_message_disables_feature (SoupMessage *msg,
 					gpointer     feature);
 #endif /* SOUP_MESSAGE_PRIVATE_H */
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 020577a..80d49af 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -139,6 +139,7 @@ soup_message_init (SoupMessage *msg)
 
 	priv->io_status = SOUP_MESSAGE_IO_STATUS_IDLE;
 	priv->http_version = priv->orig_http_version = SOUP_HTTP_1_1;
+	priv->redirection_count = 0;
 
 	msg->request_body = soup_message_body_new ();
 	msg->request_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_REQUEST);
@@ -1509,6 +1510,28 @@ soup_message_set_status_full (SoupMessage *msg,
 }
 
 void
+soup_message_increment_redirection_count (SoupMessage *msg)
+{
+	SoupMessagePrivate *priv;
+
+	g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+	priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+	priv->redirection_count++;
+}
+
+guint
+soup_message_get_redirection_count (SoupMessage *msg)
+{
+	SoupMessagePrivate *priv;
+
+	g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
+
+	priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+	return priv->redirection_count;
+}
+
+void
 soup_message_set_io_status (SoupMessage          *msg,
 			    SoupMessageIOStatus   status)
 {
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 547db9f..8331645 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -910,6 +910,11 @@ redirect_handler (SoupMessage *msg, gpointer user_data)
 						"Location");
 	g_return_if_fail (new_loc != NULL);
 
+	if (soup_message_get_redirection_count (msg) >= 20) {
+		soup_session_cancel_message (session, msg, SOUP_STATUS_CONTINOUS_REDIRECTIONS);
+	}
+	soup_message_increment_redirection_count (msg);
+
 	if (msg->status_code == SOUP_STATUS_SEE_OTHER ||
 	    (msg->status_code == SOUP_STATUS_FOUND &&
 	     !SOUP_METHOD_IS_SAFE (msg->method)) ||
diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h
index 66e9f1c..4c1239e 100644
--- a/libsoup/soup-status.h
+++ b/libsoup/soup-status.h
@@ -32,6 +32,7 @@ typedef enum {
 	SOUP_STATUS_IO_ERROR,
 	SOUP_STATUS_MALFORMED,
 	SOUP_STATUS_TRY_AGAIN,
+	SOUP_STATUS_CONTINOUS_REDIRECTIONS,
 
 	/* HTTP Status Codes */
 	SOUP_STATUS_CONTINUE                        = 100,
