diff --git a/libsoup/soup-session-async.c b/libsoup/soup-session-async.c
index 9416a7a..d79b988 100644
--- a/libsoup/soup-session-async.c
+++ b/libsoup/soup-session-async.c
@@ -182,6 +182,14 @@ resolved_proxy_uri (SoupProxyURIResolver *proxy_resolver,
 	item->resolved_proxy_addr = TRUE;
 	soup_message_queue_item_unref (item);
 
+	/* if proxy_resolver == NULL, proxy was not resolved and previous
+	   value was used */
+	if (proxy_resolver != NULL) {
+		soup_session_set_proxy_entry (item->session,
+					      soup_message_get_uri (item->msg),
+					      proxy_uri, proxy_resolver);
+	}
+
 	/* If we got here we know session still exists */
 	run_queue ((SoupSessionAsync *)session);
 }
@@ -190,15 +198,25 @@ static void
 resolve_proxy_addr (SoupMessageQueueItem *item,
 		    SoupProxyURIResolver *proxy_resolver)
 {
+	SoupURI *cached_uri;
+
 	if (item->resolving_proxy_addr)
 		return;
 	item->resolving_proxy_addr = TRUE;
 
+	cached_uri = soup_session_get_proxy_entry (item->session, soup_message_get_uri (item->msg));
+
 	soup_message_queue_item_ref (item);
-	soup_proxy_uri_resolver_get_proxy_uri_async (
-		proxy_resolver, soup_message_get_uri (item->msg),
-		soup_session_get_async_context (item->session),
-		item->cancellable, resolved_proxy_uri, item);
+
+	if (cached_uri == NULL) {
+		soup_proxy_uri_resolver_get_proxy_uri_async (
+			proxy_resolver, soup_message_get_uri (item->msg),
+			soup_session_get_async_context (item->session),
+			item->cancellable, resolved_proxy_uri, item);
+	} else {
+		resolved_proxy_uri (NULL, SOUP_STATUS_OK,
+				    cached_uri, item);
+	}
 }
 
 static void
diff --git a/libsoup/soup-session-private.h b/libsoup/soup-session-private.h
index 72b79c5..547397e 100644
--- a/libsoup/soup-session-private.h
+++ b/libsoup/soup-session-private.h
@@ -11,8 +11,15 @@
 #include "soup-message-queue.h"
 #include "soup-proxy-uri-resolver.h"
 
+#define SOUP_SESSION_PROXY_ENTRY_TTL 30
+
 G_BEGIN_DECLS
 
+SoupURI *soup_session_get_proxy_entry (SoupSession *session, SoupURI *uri);
+
+void soup_session_set_proxy_entry (SoupSession *session, SoupURI *original_uri,
+				   SoupURI *proxy_uri, SoupProxyURIResolver *resolver);
+
 /* "protected" methods for subclasses */
 SoupMessageQueue     *soup_session_get_queue            (SoupSession          *session);
 
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 5ed4fb7..264f5d9 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -83,6 +83,7 @@ typedef struct {
 
 	GHashTable *hosts; /* char* -> SoupSessionHost */
 	GHashTable *conns; /* SoupConnection -> SoupSessionHost */
+	GHashTable *proxy_uris; /* SoupURI -> SoupSessionProxyEntry */
 	guint num_conns;
 	guint max_conns, max_conns_per_host;
 	guint io_timeout, idle_timeout;
@@ -93,6 +94,8 @@ typedef struct {
 	 */
 	GMutex *host_lock;
 
+	GMutex *proxy_uris_lock;
+
 	GMainContext *async_context;
 
 	GResolver *resolver;
@@ -158,6 +161,87 @@ static void set_property (GObject *object, guint prop_id,
 static void get_property (GObject *object, guint prop_id,
 			  GValue *value, GParamSpec *pspec);
 
+typedef struct {
+	GTimeVal when;
+	SoupURI *proxy_uri;
+} SoupSessionProxyEntry;
+
+typedef struct {
+	SoupSession *session;
+	SoupURI *uri;
+} SoupSessionProxyResolutionData;
+
+static void
+free_soup_session_proxy_entry (SoupSessionProxyEntry *entry)
+{
+	g_slice_free (SoupSessionProxyEntry, entry);
+}
+
+SoupURI *
+soup_session_get_proxy_entry (SoupSession *session, SoupURI *uri)
+{
+	SoupSessionProxyEntry *data;
+	SoupURI *res = NULL;
+	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+	GTimeVal now;
+
+	g_mutex_lock (priv->proxy_uris_lock);
+	g_get_current_time (&now);
+	data = g_hash_table_lookup (priv->proxy_uris, uri);
+
+	if (data != NULL) {
+		if ((now.tv_usec - data->when.tv_sec) > SOUP_SESSION_PROXY_ENTRY_TTL) {
+			soup_uri_free (data->proxy_uri);
+			g_hash_table_remove (priv->proxy_uris, uri);
+		} else if (data->proxy_uri != NULL)
+			res = soup_uri_copy (data->proxy_uri);
+	}
+
+	g_mutex_unlock (priv->proxy_uris_lock);
+	return res;
+}
+
+void
+soup_session_set_proxy_entry (SoupSession *session,  SoupURI *original_uri,
+			      SoupURI *proxy_uri, SoupProxyURIResolver *resolver)
+{
+	SoupSessionProxyEntry *data;
+	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+	GTimeVal now;
+
+	if (resolver != SOUP_PROXY_URI_RESOLVER
+			  (soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER))) {
+		g_get_current_time (&now);
+		g_mutex_lock (priv->proxy_uris_lock);
+		data = g_slice_new (SoupSessionProxyEntry);
+		data->when = now;
+
+		if (proxy_uri != NULL)
+			proxy_uri = soup_uri_copy (proxy_uri);
+		data->proxy_uri = proxy_uri;
+
+		g_hash_table_remove (priv->proxy_uris, original_uri);
+		g_hash_table_insert (priv->proxy_uris, soup_uri_copy(original_uri), data);
+
+		g_mutex_unlock (priv->proxy_uris_lock);
+	}
+}
+
+static void
+soup_session_clean_proxy_cache (SoupSession *session)
+{
+	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+	g_mutex_lock (priv->proxy_uris_lock);
+	if (g_hash_table_size (priv->proxy_uris)) {
+		g_hash_table_destroy (priv->proxy_uris);
+		priv->proxy_uris = g_hash_table_new_full (soup_uri_host_hash,
+							  soup_uri_host_equal,
+							  (GDestroyNotify) soup_uri_free,
+							  (GDestroyNotify) free_soup_session_proxy_entry);
+	}
+	g_mutex_unlock (priv->proxy_uris_lock);
+}
+
 static void
 soup_session_init (SoupSession *session)
 {
@@ -170,6 +254,11 @@ soup_session_init (SoupSession *session)
 	priv->hosts = g_hash_table_new_full (soup_uri_host_hash,
 					     soup_uri_host_equal,
 					     NULL, (GDestroyNotify)free_host);
+	priv->proxy_uris_lock = g_mutex_new ();
+	priv->proxy_uris = g_hash_table_new_full (soup_uri_host_hash,
+						  soup_uri_host_equal,
+						  (GDestroyNotify) soup_uri_free,
+						  (GDestroyNotify) free_soup_session_proxy_entry);
 	priv->conns = g_hash_table_new (NULL, NULL);
 
 	priv->max_conns = SOUP_SESSION_MAX_CONNS_DEFAULT;
@@ -216,8 +305,10 @@ finalize (GObject *object)
 	soup_message_queue_destroy (priv->queue);
 
 	g_mutex_free (priv->host_lock);
+	g_mutex_free (priv->proxy_uris_lock);
 	g_hash_table_destroy (priv->hosts);
 	g_hash_table_destroy (priv->conns);
+	g_hash_table_destroy (priv->proxy_uris);
 
 	g_free (priv->user_agent);
 	g_free (priv->accept_language);
@@ -1738,6 +1829,45 @@ soup_session_abort (SoupSession *session)
 	g_slist_free (conns);
 }
 
+static void
+dns_prefetch (SoupSession *session, SoupURI *uri)
+{
+	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
+	SoupSessionHost *host;
+	SoupAddress *addr;
+
+	g_mutex_lock (priv->host_lock);
+	host = get_host_for_uri (session, uri);
+	addr = g_object_ref (host->addr);
+	g_mutex_unlock (priv->host_lock);
+
+	soup_address_resolve_async (addr, priv->async_context,
+				    NULL, NULL, NULL);
+}
+
+static void
+soup_session_prepare_for_uri_proxy_cb (SoupProxyURIResolver *resolver, guint status,
+				       SoupURI *proxy_uri, gpointer user_data)
+{
+	SoupSession *session;
+	SoupURI *uri;
+	SoupSessionProxyResolutionData *data = user_data;
+
+	session = data->session;
+	uri = data->uri;
+
+	soup_session_set_proxy_entry (session, uri, proxy_uri, resolver);
+
+	if (proxy_uri) {
+		dns_prefetch (session, proxy_uri);
+	} else {
+		dns_prefetch (session, uri);
+	}
+
+	soup_uri_free (uri);
+	g_slice_free (SoupSessionProxyResolutionData, data);
+}
+
 /**
 * soup_session_prepare_for_uri:
 * @session: a #SoupSession
@@ -1758,16 +1888,27 @@ void
 soup_session_prepare_for_uri (SoupSession *session, SoupURI *uri)
 {
 	SoupSessionPrivate *priv = SOUP_SESSION_GET_PRIVATE (session);
-	SoupSessionHost *host;
-	SoupAddress *addr;
-
-	g_mutex_lock (priv->host_lock);
-	host = get_host_for_uri (session, uri);
-	addr = g_object_ref (host->addr);
-	g_mutex_unlock (priv->host_lock);
-
-	soup_address_resolve_async (addr, priv->async_context,
-				    NULL, NULL, NULL);
+	SoupSessionProxyResolutionData *data;
+	SoupURI *cached_proxy_uri;
+	SoupProxyURIResolver *proxy_uri_resolver;
+
+	proxy_uri_resolver = SOUP_PROXY_URI_RESOLVER (soup_session_get_feature (session, SOUP_TYPE_PROXY_URI_RESOLVER));
+
+	if (proxy_uri_resolver != NULL) {
+		cached_proxy_uri = soup_session_get_proxy_entry (session, uri);
+		if (cached_proxy_uri == NULL) {
+			data = g_slice_new (SoupSessionProxyResolutionData);
+			data->session = session;
+			data->uri = soup_uri_copy (uri);
+			soup_proxy_uri_resolver_get_proxy_uri_async (proxy_uri_resolver, data->uri,
+								     priv->async_context, NULL,
+								     soup_session_prepare_for_uri_proxy_cb,
+								     data);
+		} else
+			dns_prefetch (session, cached_proxy_uri);
+	} else {
+		dns_prefetch (session, uri);
+	}
 }
 
 /**
@@ -1793,6 +1934,8 @@ soup_session_add_feature (SoupSession *session, SoupSessionFeature *feature)
 	priv->features = g_slist_prepend (priv->features, g_object_ref (feature));
 	g_hash_table_remove_all (priv->features_cache);
 	soup_session_feature_attach (feature, session);
+	if (SOUP_IS_PROXY_URI_RESOLVER (feature))
+		soup_session_clean_proxy_cache (session);
 }
 
 /**
@@ -1844,6 +1987,8 @@ soup_session_remove_feature (SoupSession *session, SoupSessionFeature *feature)
 		soup_session_feature_detach (feature, session);
 		g_object_unref (feature);
 	}
+	if (SOUP_IS_PROXY_URI_RESOLVER (feature))
+		soup_session_clean_proxy_cache (session);
 }
 
 /**
