diff --git a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
index f107e18..eca10e5 100644
--- a/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
+++ b/src/org/openstreetmap/josm/data/cache/JCSCachedTileLoaderJob.java
@@ -3,13 +3,10 @@
 
 import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.net.HttpURLConnection;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
@@ -26,6 +23,7 @@
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.cache.ICachedLoaderListener.LoadResult;
 import org.openstreetmap.josm.data.preferences.IntegerProperty;
+import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -310,40 +308,33 @@ private boolean loadObject() {
                 return true;
             }
 
-            HttpURLConnection urlConn = getURLConnection(getUrl(), true);
+            // TODO use #getURLConnection() instead?
+            final HttpClient request = HttpClient.create(getUrl()).useCache(false);
 
             if (isObjectLoadable()  &&
                     (now - attributes.getLastModification()) <= ABSOLUTE_EXPIRE_TIME_LIMIT) {
-                urlConn.setIfModifiedSince(attributes.getLastModification());
+                request.setIfModifiedSince(attributes.getLastModification());
             }
             if (isObjectLoadable() && attributes.getEtag() != null) {
-                urlConn.addRequestProperty("If-None-Match", attributes.getEtag());
+                request.setHeader("If-None-Match", attributes.getEtag());
             }
 
-            log.log(Level.INFO, "GET {0} -> {1}", new Object[]{getUrl(), urlConn.getResponseCode()});
+            final HttpClient.Response urlConn = request.connect();
 
-            // follow redirects
-            for (int i = 0; i < 5; i++) {
-                if (urlConn.getResponseCode() == 302) {
-                    urlConn = getURLConnection(new URL(urlConn.getHeaderField("Location")), true);
-                } else {
-                    break;
-                }
-            }
-            if (urlConn.getResponseCode() == 304) {
+            if (urlConn.getResponseCode() == 302) {
                 // If isModifiedSince or If-None-Match has been set
                 // and the server answers with a HTTP 304 = "Not Modified"
                 log.log(Level.FINE, "JCS - IfModifiedSince/Etag test: local version is up to date: {0}", getUrl());
                 return true;
-            } else if (isObjectLoadable() // we have an object in cache, but we haven't received 304 resposne code
+            } else if (isObjectLoadable() // we have an object in cache, but we haven't received 304 response code
                     && (
-                            (attributes.getEtag() != null && attributes.getEtag().equals(urlConn.getRequestProperty("ETag"))) ||
+                            (attributes.getEtag() != null && attributes.getEtag().equals(urlConn.getHeaderField("ETag"))) ||
                             attributes.getLastModification() == urlConn.getLastModified())
                     ) {
                 // we sent ETag or If-Modified-Since, but didn't get 304 response code
                 // for further requests - use HEAD
                 String serverKey = getServerKey();
-                log.log(Level.INFO, "JCS - Host: {0} found not to return 304 codes for If-Modifed-Since or If-None-Match headers",
+                log.log(Level.INFO, "JCS - Host: {0} found not to return 304 codes for If-Modified-Since or If-None-Match headers",
                         serverKey);
                 useHead.put(serverKey, Boolean.TRUE);
             }
@@ -360,7 +351,7 @@ private boolean loadObject() {
                 attributes.setResponseCode(urlConn.getResponseCode());
                 byte[] raw;
                 if (urlConn.getResponseCode() == 200) {
-                    raw = Utils.readBytesFromStream(urlConn.getInputStream());
+                    raw = Utils.readBytesFromStream(urlConn.getContent());
                 } else {
                     raw = new byte[]{};
                 }
@@ -433,7 +424,7 @@ protected boolean isResponseLoadable(Map<String, List<String>> headerFields, int
 
     protected abstract V createCacheEntry(byte[] content);
 
-    protected CacheEntryAttributes parseHeaders(URLConnection urlConn) {
+    protected CacheEntryAttributes parseHeaders(HttpClient.Response urlConn) {
         CacheEntryAttributes ret = new CacheEntryAttributes();
 
         Long lng = urlConn.getExpiration();
@@ -460,44 +451,28 @@ protected CacheEntryAttributes parseHeaders(URLConnection urlConn) {
         ret.setLastModification(now);
         ret.setEtag(urlConn.getHeaderField("ETag"));
 
-        if (Main.isDebugEnabled()) {
-            for (Entry<String, List<String>> header: urlConn.getHeaderFields().entrySet()) {
-                log.log(Level.FINE, "Response header - {0}: {1}", new Object[]{header.getKey(), header.getValue()});
-            }
-        }
-
         return ret;
     }
 
-    private HttpURLConnection getURLConnection(URL url, boolean noCache) throws IOException {
-        HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
-        urlConn.setRequestProperty("Accept", "text/html, image/png, image/jpeg, image/gif, */*");
+    private HttpClient getURLConnection(URL url, boolean noCache, String requestMethod) throws IOException {
+        final HttpClient urlConn = HttpClient.create(url, requestMethod);
+        urlConn.setAccept("text/html, image/png, image/jpeg, image/gif, */*");
         urlConn.setReadTimeout(readTimeout); // 30 seconds read timeout
         urlConn.setConnectTimeout(connectTimeout);
         if (headers != null) {
-            for (Map.Entry<String, String> e: headers.entrySet()) {
-                urlConn.setRequestProperty(e.getKey(), e.getValue());
-            }
+            urlConn.setHeaders(headers);
         }
 
         if (force || noCache) {
-            urlConn.setUseCaches(false);
+            urlConn.useCache(false);
         }
         return urlConn;
     }
 
     private boolean isCacheValidUsingHead() throws IOException {
-        HttpURLConnection urlConn = getURLConnection(getUrl(), false);
-        urlConn.setRequestMethod("HEAD");
-        for (int i = 0; i < 5; i++) {
-            if (urlConn.getResponseCode() == 302) {
-                urlConn = getURLConnection(new URL(urlConn.getHeaderField("Location")), false);
-            } else {
-                break;
-            }
-        }
+        final HttpClient.Response urlConn = getURLConnection(getUrl(), false, "HEAD").connect();
         long lastModified = urlConn.getLastModified();
-        return (attributes.getEtag() != null && attributes.getEtag().equals(urlConn.getRequestProperty("ETag"))) ||
+        return (attributes.getEtag() != null && attributes.getEtag().equals(urlConn.getHeaderField("ETag"))) ||
                 (lastModified != 0 && lastModified <= attributes.getLastModification());
     }
 
diff --git a/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java b/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
index 64c933c..631b5ef 100644
--- a/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
+++ b/src/org/openstreetmap/josm/data/imagery/TMSCachedTileLoaderJob.java
@@ -6,7 +6,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.net.URL;
-import java.net.URLConnection;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
@@ -30,6 +29,7 @@
 import org.openstreetmap.josm.data.cache.CacheEntryAttributes;
 import org.openstreetmap.josm.data.cache.ICachedLoaderListener;
 import org.openstreetmap.josm.data.cache.JCSCachedTileLoaderJob;
+import org.openstreetmap.josm.tools.HttpClient;
 
 /**
  * @author Wiktor Niesiobędzki
@@ -239,7 +239,7 @@ public void submit() {
     }
 
     @Override
-    protected CacheEntryAttributes parseHeaders(URLConnection urlConn) {
+    protected CacheEntryAttributes parseHeaders(HttpClient.Response urlConn) {
         CacheEntryAttributes ret = super.parseHeaders(urlConn);
         // keep the expiration time between MINIMUM_EXPIRES and MAXIMUM_EXPIRES, so we will cache the tiles
         // at least for some short period of time, but not too long
diff --git a/src/org/openstreetmap/josm/tools/HttpClient.java b/src/org/openstreetmap/josm/tools/HttpClient.java
index 300c2b8..31ea2ba 100644
--- a/src/org/openstreetmap/josm/tools/HttpClient.java
+++ b/src/org/openstreetmap/josm/tools/HttpClient.java
@@ -309,6 +309,7 @@ public String getResponseMessage() {
         /**
          * Returns the {@code Content-Encoding} header.
          * @return {@code Content-Encoding} HTTP header
+         * @see HttpURLConnection#getContentEncoding()
          */
         public String getContentEncoding() {
             return connection.getContentEncoding();
@@ -323,8 +324,27 @@ public String getContentType() {
         }
 
         /**
+         * Returns the {@code Expire} header.
+         * @return {@code Expire} HTTP header
+         * @see HttpURLConnection#getExpiration()
+         */
+        public long getExpiration() {
+            return connection.getExpiration();
+        }
+
+        /**
+         * Returns the {@code Last-Modified} header.
+         * @return {@code Last-Modified} HTTP header
+         * @see HttpURLConnection#getLastModified()
+         */
+        public long getLastModified() {
+            return connection.getLastModified();
+        }
+
+        /**
          * Returns the {@code Content-Length} header.
          * @return {@code Content-Length} HTTP header
+         * @see HttpURLConnection#getContentLengthLong()
          */
         public long getContentLength() {
             return connection.getContentLengthLong();
@@ -353,6 +373,15 @@ public String getHeaderField(String name) {
         }
 
         /**
+         * Returns an unmodifiable Map mapping header keys to a List of header values.
+         * @return unmodifiable Map mapping header keys to a List of header values
+         * @see HttpURLConnection#getHeaderFields()
+         */
+        public Map<String, List<String>> getHeaderFields() {
+            return connection.getHeaderFields();
+        }
+
+        /**
          * @see HttpURLConnection#disconnect()
          */
         public void disconnect() {
