Setup guide
Java HTTP proxy authentication with HttpClient
The HttpClient bundled with the JDK takes its proxy from a ProxySelector and answers the proxy’s login challenge through an Authenticator. One JDK default gets in the way: Basic authentication is switched off for HTTPS tunnels, so a first attempt ends in 407 until one system property changes. Everything below was run on Temurin JDK 25.
Before you start
- A JDK, release 11 or later. HttpClient and launching a single source file with the java command both arrived in Java 11; the examples were tested on 25.
- An empty folder and a terminal. No Maven or Gradle project is needed.
- HOST, PORT, USERNAME and PASSWORD, listed on the service page in your dashboard.
Your connection details
Sign in to the dashboard and open the service you bought. Its page shows the host, port, username and password for that service. There is no single ProxyPanda address to remember, so copy them from there each time. The code in these guides uses the placeholders below; replace each one with your own value.
- HOST
- The proxy address for this service, exactly as the service page shows it.
- PORT
- The port to connect to. Copy it with the host, since it can differ from one service to the next.
- USERNAME
- Your proxy login. It is separate from the email you use for the dashboard.
- PASSWORD
- Copy it in full. On residential, the options you choose in the dashboard are added to the end of the password, so a password typed from memory loses them.
You can skip the username and password by adding the IP address you connect from to the service’s allowlist. Then only HOST and PORT go into your code.
The examples use HTTP, which reaches https sites through an encrypted tunnel. SOCKS5 works too: residential ports accept both, and ISP and datacenter proxies switch protocol in the dashboard.
Steps
Check the JDK and make a folder
java -version shows which release will compile and run the files. Each example is a complete source file: save it in this folder and start it with java followed by its file name.
terminal java -version mkdir proxycheck && cd proxycheckBuild a client with a proxy and an Authenticator
ProxySelector.of sends every request to one proxy address. The Authenticator hands over the login only when the requestor type is PROXY, so a site asking for a password never receives the proxy’s. connectTimeout limits how long reaching the proxy may take, and the timeout on the request bounds the whole wait for an answer.
CheckIp.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 CheckIp { public static void main(String[] args) throws Exception { HttpClient client = HttpClient.newBuilder() .proxy(ProxySelector.of(new InetSocketAddress("HOST", PORT))) .authenticator(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { if (getRequestorType() != RequestorType.PROXY) return null; return new PasswordAuthentication("USERNAME", "PASSWORD".toCharArray()); } }) .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 Basic allowed for tunnels
The JDK’s conf/net.properties sets jdk.http.auth.tunneling.disabledSchemes to Basic, so the client refuses to put a username and password on the CONNECT request that opens an HTTPS tunnel. Clearing that list on the command line lets the login through, and the program prints 200 with the JSON from api.ipify.org. Without the flag, the same file printed 407 null.
terminal java -Djdk.http.auth.tunneling.disabledSchemes="" CheckIp.javaOr clear the property from code
When you cannot change how the program is launched, make System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "") the first line of main. The rotation example does exactly that. Editing the line in conf/net.properties also worked in testing, but it changes the rule for every program that JDK runs.
Spread requests over several static IPs
ISP and datacenter services give you a set of fixed addresses. Put them in proxies.txt, one HOST:PORT:USERNAME:PASSWORD line each, and start the file with java Rotate.java. It builds one HttpClient per line, each with its own Authenticator, and gives request i to client i modulo the list length. A proxy that fails prints its exception while the loop carries on.
Rotate.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.nio.file.Files; import java.nio.file.Path; import java.time.Duration; import java.util.ArrayList; import java.util.List; public class Rotate { static HttpClient clientFor(String line) { String[] p = line.trim().split(":", 4); return HttpClient.newBuilder() .proxy(ProxySelector.of(new InetSocketAddress(p[0], Integer.parseInt(p[1])))) .authenticator(new Authenticator() { @Override protected PasswordAuthentication getPasswordAuthentication() { if (getRequestorType() != RequestorType.PROXY) return null; return new PasswordAuthentication(p[2], p[3].toCharArray()); } }) .connectTimeout(Duration.ofSeconds(10)) .build(); } public static void main(String[] args) throws Exception { System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); List<HttpClient> clients = new ArrayList<>(); for (String line : Files.readAllLines(Path.of("proxies.txt"))) { if (!line.isBlank()) clients.add(clientFor(line)); } for (int i = 0; i < 6; i++) { HttpClient client = clients.get(i % clients.size()); HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.ipify.org")) .timeout(Duration.ofSeconds(30)) .build(); try { System.out.println(i + " " + client.send(request, HttpResponse.BodyHandlers.ofString()).body()); } catch (Exception e) { System.out.println(i + " " + e); } } } }
Rotating and sticky IPs
An HttpClient keeps its tunnel open between requests. In testing, three requests from one client went through a single CONNECT, and a reused tunnel keeps one exit address. That suits Sticky IP and ISP or datacenter IPs.
On residential with Randomize IP, a fresh tunnel is what brings a fresh address. Launching with -Djdk.httpclient.keepalive.timeout=0 made the client open a new tunnel for each of three requests on JDK 25. Building a new HttpClient per request has the same effect at the cost of a new handshake every time.
An HttpClient is immutable once built and sends as many requests as you like, so create one per proxy at startup and share it.
Common errors and fixes
The program prints 407 null
The proxy asked for a login and the client never offered one, because Basic is still disabled for tunnels. Put -Djdk.http.auth.tunneling.disabledSchemes="" before the file name: placed after CheckIp.java it reaches main as an argument and changes nothing.
An IOException after several 407 replies
The Authenticator answered, and the proxy kept rejecting what it sent. Against the test proxy the client tried four times and then threw, on JDK 25 with a NullPointerException as the cause that points nowhere useful. Paste USERNAME and PASSWORD from the service page again.
ConnectException with no message
Nothing accepted a connection at HOST and PORT. Hold both against the service page, and confirm in the dashboard that the service is still active.
HttpConnectTimeoutException: HTTP connect timed out
The connection or the tunnel was not ready in time. A slow response instead raises HttpTimeoutException once the request timeout passes. Residential exits are home connections and can lag, so give them more time and retry only the failed URL.
Which line to pick
Java crawlers and API pollers aimed at sites that accept server traffic run cheapest on datacenter IPs, with Rotate.java sharing the load between them. Move to residential when requests from server ranges start failing, and keep an ISP IP for a service that must hold one address for weeks.
Other setup guides
Not sure what a word means? The glossary explains it in plain English.
Stuck on a step?
Paste the command and the error into Discord, with your password taken out. People there have met most of these errors before.