All posts
Tutorials4 min readOct 11, 2026

Proxies in Java: HttpClient, OkHttp and the Basic-auth gotcha

Use authenticated proxies from Java 11+ HttpClient and OkHttp. Why HTTPS requests fail with 407 even though your password is right, the one-line fix, plus sticky sessions and rotation.

By crawlproxies

Java can use authenticated proxies with no extra libraries, but it has one trap that catches almost everyone: out of the box, the JDK refuses to send a username and password to a proxy when it opens an HTTPS tunnel. The request fails with 407 Proxy Authentication Required even though the credentials are correct. This guide shows the fix, a working HttpClient setup, the same thing in OkHttp, and how to get sticky or rotating IPs.

Your username and password are in the generator. The examples use the Residential gateway geo.crawlproxies.com:8080.

The 407 gotcha

Since Java 8u111, Basic authentication is disabled for HTTPS tunnelling by default (the jdk.http.auth.tunneling.disabledSchemes property is set to Basic). Proxies use Basic authentication, so you have to allow it. Start the JVM with:

bash
java -Djdk.http.auth.tunneling.disabledSchemes="" -jar app.jar

Or set it at the very start of main, before the first request is made:

java
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");

The JVM reads the property once, when the HTTP code is first loaded, so setting it later has no effect. The command-line flag is the safest place.

HttpClient (Java 11+)

java
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 ProxyDemo {
    public static void main(String[] args) throws Exception {
        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");

        HttpClient client = HttpClient.newBuilder()
            .proxy(ProxySelector.of(new InetSocketAddress("geo.crawlproxies.com", 8080)))
            .authenticator(proxyLogin("USERNAME", "PASSWORD"))
            .connectTimeout(Duration.ofSeconds(20))
            .build();

        HttpRequest request = HttpRequest.newBuilder(URI.create("https://ipinfo.io/json"))
            .timeout(Duration.ofSeconds(30))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.statusCode() + " " + response.body());
    }

    static Authenticator proxyLogin(String user, String password) {
        return new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                if (getRequestorType() != RequestorType.PROXY) {
                    return null;    // never hand the proxy login to a website
                }
                return new PasswordAuthentication(user, password.toCharArray());
            }
        };
    }
}

The RequestorType.PROXY check matters: without it, the same credentials would be sent to any website that asks for a login.

Java's HttpClient speaks HTTP proxies only, so use port 8080 rather than the SOCKS5 port.

OkHttp

OkHttp doesn't have the JDK's Basic-auth restriction. Give it the proxy and a proxyAuthenticator that answers the 407 challenge:

java
import java.net.InetSocketAddress;
import java.net.Proxy;
import okhttp3.Credentials;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("geo.crawlproxies.com", 8080)))
    .proxyAuthenticator((route, response) -> response.request().newBuilder()
        .header("Proxy-Authorization", Credentials.basic("USERNAME", "PASSWORD"))
        .build())
    .build();

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

Sticky sessions and geo-targeting

Targeting options go in the username. Build one client per identity:

java
String session = java.util.UUID.randomUUID().toString().substring(0, 8);
HttpClient sticky = HttpClient.newBuilder()
    .proxy(ProxySelector.of(new InetSocketAddress("geo.crawlproxies.com", 8080)))
    .authenticator(proxyLogin("USERNAME-country-us-session-" + session + "-time-1800", "PASSWORD"))
    .build();

That client keeps one US IP for up to 30 minutes. The geo-targeting guide lists every option, and sticky vs rotating sessions explains when to use which.

Making IPs rotate

With a rotating username, every new connection gets a new IP. Both HttpClient and OkHttp keep connections open and reuse them, so requests to the same site through one client can share an IP. For a new IP per request:

  • OkHttp: add .connectionPool(new ConnectionPool(0, 1, TimeUnit.SECONDS)) to the builder so no idle connection is kept.
  • HttpClient: it has no per-client switch for this. Create a new client per request for small jobs, or give each worker its own client and recreate it every few requests.

Errors you might see

ErrorUsually means
407 on HTTPS URLs onlyThe tunnelling property from the first section isn't set (or is set too late)
407 everywhereWrong username or password, or a typo in the targeting part
HttpConnectTimeoutExceptionWrong host or port, or a firewall in the way
403 / 429 from the siteBlocking or rate limiting: see the debugging checklist

Testing a line in the terminal first saves a lot of guessing: the cURL cheat sheet has the commands.

Written by
crawlproxies
Create account