A Java HTTP proxy is configured on the client builder: HttpClient.newBuilder().proxy(ProxySelector.of(address)) plus an Authenticator that returns your proxy username and password. For HTTPS sites you also need one system property, jdk.http.auth.tunneling.disabledSchemes="". Without it the JDK refuses to send Basic credentials inside a CONNECT tunnel and every HTTPS request gets a 407.

Every snippet on this page ran on 2026-09-30 with Temurin OpenJDK 21.0.12 and OkHttp 5.5.0, against local authenticating HTTP proxies and a SOCKS5 proxy that enforces username and password and logs each request.

Before you start: copy your proxy details

  1. Open your order in the dashboard at https://app.proxyhive.io.
  2. Copy HOST, PORT, USERNAME and PASSWORD. Each ISP or datacenter IP is its own endpoint, with its own HTTP, HTTPS and SOCKS5 ports listed on the order. Use the HTTP port for the proxies below.
  3. Export them rather than hard-coding them:
export PROXY_HOST=HOST PROXY_PORT=PORT PROXY_USER=USERNAME PROXY_PASS=PASSWORD

No IP yet? Static ISP proxies are sold one address at a time.

Java HTTP proxy with HttpClient and an Authenticator

import java.net.Authenticator;
import java.net.InetSocketAddress;
import java.net.PasswordAuthentication;
import java.net.ProxySelector;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.time.Duration;

public class ProxyClient {
    public static void main(String[] args) throws Exception {
        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");

        String user = System.getenv("PROXY_USER");
        char[] pass = System.getenv("PROXY_PASS").toCharArray();

        HttpClient client = HttpClient.newBuilder()
                .proxy(ProxySelector.of(new InetSocketAddress(
                        System.getenv("PROXY_HOST"), Integer.parseInt(System.getenv("PROXY_PORT")))))
                .authenticator(new Authenticator() {
                    @Override
                    protected PasswordAuthentication getPasswordAuthentication() {
                        if (getRequestorType() == RequestorType.PROXY) {
                            return new PasswordAuthentication(user, pass);
                        }
                        return null;
                    }
                })
                .connectTimeout(Duration.ofSeconds(10))
                .build();

        HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.ipify.org?format=json"))
                .timeout(Duration.ofSeconds(30))
                .build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }
}

Run it with java ProxyClient.java. Checking getRequestorType() keeps the authenticator from handing your proxy password to a target site that asks for credentials of its own.

The HTTPS trap: jdk.http.auth.tunneling.disabledSchemes

Remove the System.setProperty line and the same program prints 407 null for the HTTPS URL, while an http:// URL through the same proxy works. The JDK ships with jdk.http.auth.tunneling.disabledSchemes=Basic in conf/net.properties, so it will not answer a Basic challenge on a CONNECT request. Both routes to fixing it worked in our run:

java -Djdk.http.auth.tunneling.disabledSchemes= ProxyClient.java

or System.setProperty as the first line of main, before any client exists. Note that HttpClient reports the failure as a response with status 407, not an exception, so check statusCode().

The trap is wider than HttpClient. The classic HttpURLConnection with url.openConnection(proxy) and Authenticator.setDefault fails the same way on HTTPS, as IOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 Proxy Authentication Required", and the same property fixes it. The HttpClient API documentation covers the builder; the property lives in the JDK's networking properties.

One more observation from the proxy log: HttpClient sends the CONNECT without credentials first and only authenticates after the 407. That costs one extra round trip per new connection, which is another reason to reuse clients.

OkHttp proxy with proxyAuthenticator

OkHttp needs no JDK property, because it builds the Proxy-Authorization header itself. Gradle users add com.squareup.okhttp3:okhttp:5.5.0; Maven users need the okhttp-jvm artifact, since the plain okhttp artifact is an empty jar under Maven (we downloaded it: 754 bytes).

String credential = Credentials.basic(System.getenv("PROXY_USER"), System.getenv("PROXY_PASS"));
Proxy proxy = new Proxy(Proxy.Type.HTTP,
        new InetSocketAddress(System.getenv("PROXY_HOST"), Integer.parseInt(System.getenv("PROXY_PORT"))));

OkHttpClient client = new OkHttpClient.Builder()
        .proxy(proxy)
        .proxyAuthenticator((route, response) -> {
            if (response.request().header("Proxy-Authorization") != null) {
                return null;
            }
            return response.request().newBuilder()
                    .header("Proxy-Authorization", credential)
                    .build();
        })
        .connectTimeout(Duration.ofSeconds(10))
        .callTimeout(Duration.ofSeconds(30))
        .build();

Request request = new Request.Builder().url("https://api.ipify.org?format=json").build();
try (Response response = client.newCall(request).execute()) {
    System.out.println(response.code() + " " + response.body().string());
}

Returning null when the header is already present stops a retry loop on a wrong password. Our proxy log showed OkHttp sending credentials on the very first CONNECT, with no 407 round trip.

Java SOCKS5 proxy

java.net.http.HttpClient has no SOCKS support. We gave it a ProxySelector returning a SOCKS proxy: the SOCKS server saw nothing and the request succeeded directly, from our own IP. If you rely on SOCKS for anything, that silent fallback is the worst possible failure mode, so use OkHttp:

Authenticator.setDefault(new Authenticator() {
    @Override
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication(System.getenv("PROXY_USER"), System.getenv("PROXY_PASS").toCharArray());
    }
});
Proxy socks = new Proxy(Proxy.Type.SOCKS,
        InetSocketAddress.createUnresolved(System.getenv("PROXY_HOST"), Integer.parseInt(System.getenv("SOCKS5_PORT"))));
OkHttpClient client = new OkHttpClient.Builder().proxy(socks).build();

SOCKS5 credentials come from the JVM-wide Authenticator, not proxyAuthenticator. OkHttp sent the hostname to our SOCKS server, so DNS resolved on the proxy. HttpURLConnection over the same SOCKS proxy also worked but sent a locally resolved IPv4 address instead. HTTP vs SOCKS5 proxies explains when that difference matters.

Rotate across several static IPs

Build one HttpClient per endpoint and pick one per request. Each client keeps its own connection pool, so rotation is real and connections stay warm:

static HttpClient clientFor(URI proxy) {
    String[] userPass = proxy.getUserInfo().split(":", 2);
    return HttpClient.newBuilder()
            .proxy(ProxySelector.of(new InetSocketAddress(proxy.getHost(), proxy.getPort())))
            .authenticator(new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    return getRequestorType() == RequestorType.PROXY
                            ? new PasswordAuthentication(userPass[0], userPass[1].toCharArray())
                            : null;
                }
            })
            .connectTimeout(Duration.ofSeconds(10))
            .build();
}

List<HttpClient> clients = List.of(System.getenv("PROXY_URLS").split(",")).stream()
        .map(URI::create).map(ProxyRotation::clientFor).toList();
AtomicLong next = new AtomicLong();
HttpClient client = clients.get((int) (next.getAndIncrement() % clients.size()));

PROXY_URLS holds http://USER:PASS@HOST:PORT entries separated by commas. Four requests over two proxies produced one authenticated tunnel per proxy in our logs, each reused for the second request. Static ISP and datacenter IPs stay the same for the term, so the rotation order is entirely yours.

Common errors

SymptomCauseFix
407 status on HTTPS URLs, HTTP URLs fineBasic disabled for tunnelsjdk.http.auth.tunneling.disabledSchemes=""
Unable to tunnel through proxy ... 407Same, from HttpURLConnectionSame property, or switch to OkHttp
407 on every URLWrong credentialsRe-copy them from the order
SOCKS proxy ignored, direct trafficHttpClient has no SOCKS supportOkHttp with Proxy.Type.SOCKS
package okhttp3 does not exist under MavenEmpty okhttp artifactDepend on okhttp-jvm

Errors the target returns after a working tunnel (403, 429) are explained in proxy error codes.

Next steps

  • Check the endpoint outside the JVM with cURL.
  • See how the same setup looks in Go.
  • Compare line rates on ISP pricing, or read the docs for copy formats.