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:
java -Djdk.http.auth.tunneling.disabledSchemes="" -jar app.jarOr set it at the very start of main, before the first request is made:
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+)
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:
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:
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
| Error | Usually means |
|---|---|
407 on HTTPS URLs only | The tunnelling property from the first section isn't set (or is set too late) |
407 everywhere | Wrong username or password, or a typo in the targeting part |
HttpConnectTimeoutException | Wrong host or port, or a firewall in the way |
403 / 429 from the site | Blocking 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.



