Java proxy authentication trips up almost everyone the first time, because of one default most people don’t know about: since Java 8u111, the runtime refuses to send Basic proxy credentials when it opens a tunnel to an HTTPS site. This guide shows working code for the modern java.net.http.HttpClient and the classic HttpURLConnection, the one setting that fixes 407 errors on HTTPS, and how to use rotating residential proxies from Java.
The short version
Set the proxy on the client, supply the username and password through an Authenticator that answers proxy requests, and start the JVM with -Djdk.http.auth.tunneling.disabledSchemes="" so Basic authentication is allowed for HTTPS tunnels. Without that last setting, Java gets a 407 on every HTTPS request through an authenticated proxy, even with the right credentials.
How Java Handles Proxies
Where the proxy goes, and where the password goesJava separates two things that other languages often combine: the proxy address and the proxy credentials. The address can come from a java.net.Proxy object, a ProxySelector, or system properties such as https.proxyHost. Oracle’s networking guide documents all three. The credentials never go in any of those. They come from a java.net.Authenticator, which Java calls when a proxy (or a site) asks for a login.
When your code connects through a proxy that needs a login, the proxy answers with 407 Proxy Authentication Required. Java then asks the Authenticator for credentials, checks that it’s allowed to use the authentication scheme the proxy asked for, and retries the request. If any step is missing, the request fails with a 407.
proxy address -> Proxy / ProxySelector / -Dhttps.proxyHost
credentials -> Authenticator (answers RequestorType.PROXY)
HTTPS tunnels -> jdk.http.auth.tunneling.disabledSchemes must allow Basic
The Setting That Causes Most Java 407 Errors
Basic auth is disabled for HTTPS tunnels by defaultTo reach an HTTPS site through an HTTP proxy, the client first asks the proxy to open a tunnel with a CONNECT request, and authenticates to the proxy at that point. Oracle’s Java 8u111 release notes explain that the Basic authentication scheme was deactivated by default for this step, by adding Basic to the jdk.http.auth.tunneling.disabledSchemes networking property. Proxies that need Basic authentication to set up an HTTPS tunnel no longer succeed by default.
Most proxy services, ProxyEmpire included, use username and password authentication with Basic, so this default blocks them. The release notes give the fix: remove Basic from the property, or set a system property of the same name to an empty value on the command line.
# on the command line (recommended)
java -Djdk.http.auth.tunneling.disabledSchemes="" -jar app.jar
# or in code, before the first HTTP connection is made
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
The modern java.net.http.HttpClient honours the same property; its module documentation lists jdk.http.auth.tunneling.disabledSchemes among the networking properties it respects, along with jdk.http.auth.proxying.disabledSchemes for plain HTTP proxying. The setting is read early, so the command-line option is the safest place for it. If you set it in code, do it at the very start of main, before any HTTP request.
Java Proxy Authentication with HttpClient (Java 11+)
The modern clientSince Java 11, java.net.http.HttpClient is the standard way to make HTTP requests. Give it a ProxySelector for the address and an Authenticator for the credentials:
import java.net.*;
import java.net.http.*;
public class ProxyExample {
public static void main(String[] args) throws Exception {
// needed for HTTPS through a proxy that uses Basic auth
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
String user = System.getenv("PROXY_USER");
String pass = System.getenv("PROXY_PASS");
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(new InetSocketAddress("v2.proxyempire.io", 5000)))
.authenticator(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(user, pass.toCharArray());
}
return null;
}
})
.build();
HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.ipify.org")).build();
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println(response.body()); // the proxy's IP, not yours
}
}
- Check
getRequestorType(). Returning credentials only forRequestorType.PROXYkeeps your proxy password from being sent to websites that ask for their own login. - Build the client once and reuse it.
HttpClientkeeps a connection pool; creating one per request wastes it. - One client per proxy session. The authenticator belongs to the client, so use a separate client for each username when you need several sessions or locations at once.
Java Proxy Authentication with HttpURLConnection
Older code and librariesPlenty of existing code, and many libraries, still use HttpURLConnection. Here the proxy is passed to openConnection(), and the credentials come from a default Authenticator set for the whole JVM:
import java.io.*;
import java.net.*;
public class LegacyProxyExample {
public static void main(String[] args) throws Exception {
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
if (getRequestorType() == RequestorType.PROXY) {
return new PasswordAuthentication(
System.getenv("PROXY_USER"),
System.getenv("PROXY_PASS").toCharArray());
}
return null;
}
});
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("v2.proxyempire.io", 5000));
URL url = URI.create("https://api.ipify.org").toURL();
HttpURLConnection conn = (HttpURLConnection) url.openConnection(proxy);
try (BufferedReader in = new BufferedReader(
new InputStreamReader(conn.getInputStream()))) {
System.out.println(in.readLine());
}
}
}
If the proxy login fails, getInputStream() throws an IOException; for HTTPS the message usually says the connection was unable to tunnel through the proxy. Check the response code and the credentials before anything else.
Authenticator.setDefault() applies to every connection in the JVM, so keep the RequestorType.PROXY check. URI.create(...).toURL() replaces the new URL(String) constructor, which is deprecated in recent Java versions.
Java Proxy Settings with System Properties
For code you can’t changeWhen a library makes its own connections and doesn’t let you pass a proxy, you can often set the proxy for the whole JVM with the system properties from Oracle’s networking guide. They set the address only. You still need a default Authenticator for the credentials, and the tunneling setting for HTTPS.
java -Dhttp.proxyHost=v2.proxyempire.io -Dhttp.proxyPort=5000 \
-Dhttps.proxyHost=v2.proxyempire.io -Dhttps.proxyPort=5000 \
-Dhttp.nonProxyHosts="127.0.0.1|*.internal.example.com" \
-Djdk.http.auth.tunneling.disabledSchemes="" \
-jar app.jar
Setting http.proxyUser and http.proxyPassword is a common suggestion online, but those aren’t standard JDK properties; the JDK takes proxy credentials from an Authenticator. Some libraries read their own properties, so check your library’s documentation.
Java also supports SOCKS proxies with socksProxyHost and socksProxyPort, or Proxy.Type.SOCKS. For HTTP clients, the HTTP proxy endpoint with the setup above is the simplest option, and ProxyEmpire supports HTTP, HTTPS and SOCKS5 on the same account.
HttpClient or HttpURLConnection?
Which one to use for new codeFor new code on Java 11 or later, use java.net.http.HttpClient. It supports HTTP/2, asynchronous requests with sendAsync(), and it keeps its proxy and authenticator on the client, so two clients can use two different proxy sessions side by side without touching global state. That last point matters for proxy work: with HttpURLConnection, the default Authenticator is shared by the whole JVM, which makes several sets of proxy credentials awkward.
Keep HttpURLConnection for existing code and for libraries built on it. If one of those libraries needs a proxy and you can’t pass one in, the system properties below plus a default Authenticator are often the only option. Many popular HTTP libraries also accept a java.net.Proxy or a ProxySelector and their own authenticator; check the library’s documentation for how it answers a 407, because each one does it slightly differently.
Proxies in multi-threaded scrapers
A single HttpClient is safe to share between threads, so a common design is one client per proxy session and a thread pool that sends requests through them. If you need many locations at once, keep a map from location to client, each with its own username. For large jobs, prefer the asynchronous API: sendAsync() lets a few threads keep many requests in flight, and ProxyEmpire has no limit on concurrent sessions.
Set timeouts on both the client (connectTimeout) and each request (HttpRequest.Builder.timeout), so a slow proxy connection doesn’t hold a thread forever. Retry failed requests a limited number of times with a short delay, and log the proxy session each request used, so you can see if one session is misbehaving.
Keeping proxy credentials safe
The examples in this guide read the username and password from environment variables, and that’s the minimum. Never hard-code them or commit them to a repository, and keep them out of log messages and exception reports. Anyone who has them can spend your bandwidth, because ProxyEmpire proxies authenticate with the username and password, not with your IP address. If credentials leak, change them in the dashboard and update your configuration.
Rotating IPs and Sticky Sessions from Java
Pick the behaviour in the dashboardWith ProxyEmpire, the rotation mode and location are part of your proxy username, which you set up in the Proxy Manager. Your Java code doesn’t change: the same host and port, a different username.
When you change targeting in the dashboard, the username changes too. Update the value your application reads, or you’ll get 407 errors with the old one. Rotating mobile proxies work the same way from Java, on 4G carrier networks, from $2.00/GB.
Troubleshooting Java Proxy Errors
The errors we see most| What you see | Likely cause and fix |
|---|---|
| 407 on HTTPS, HTTP works | Basic is disabled for HTTPS tunnels. Start the JVM with -Djdk.http.auth.tunneling.disabledSchemes="". |
| 407 on everything | No Authenticator, or it doesn’t answer RequestorType.PROXY, or the username is out of date. Copy the credentials again from the Proxy Manager. |
| “Unable to tunnel through proxy” | An IOException from the HTTPS tunnel, usually wrapping the 407 above. Apply the tunneling setting and check the credentials. |
| Setting the property in code has no effect | It was set after the first connection. Move it to the command line or the very start of main. |
| Your own IP shows up | The proxy wasn’t applied to that client or connection. Check the ProxySelector or the openConnection(proxy) call. |
| Some sites never load | Financial, government and other high-risk sites are blocked on the network, and only ports 80 and 443 are open by default. |
A quick way to separate credential problems from code problems is to test the same host, port, username and password with curl: curl -x http://v2.proxyempire.io:5000 -U "USERNAME:PASSWORD" https://api.ipify.org. If curl works and Java doesn’t, the issue is in the Java setup. Our guide to 407 Proxy Authentication Required covers the error in more depth, and the proxy troubleshooting guide covers everything else.
Java Proxy Authentication FAQ
Quick answersHow do I set proxy authentication in Java?
Set the proxy address on the client or connection, and supply the username and password through a java.net.Authenticator that returns credentials when getRequestorType() is RequestorType.PROXY.
Why does Java return 407 for HTTPS even with the right password?
Since Java 8u111, Basic authentication is disabled for HTTPS tunnels by default. Start the JVM with -Djdk.http.auth.tunneling.disabledSchemes="" to allow it.
Does java.net.http.HttpClient support proxy authentication?
Yes. Pass a ProxySelector to .proxy() and an Authenticator to .authenticator() on the builder. It honours the same tunneling property.
Are http.proxyUser and http.proxyPassword valid Java properties?
Not in the standard JDK. Some libraries read them, but the JDK’s own HTTP clients take proxy credentials from an Authenticator.
Do I need the tunneling setting for plain HTTP sites?
No. The setting only affects the tunnel Java opens for HTTPS sites. Plain HTTP requests through the proxy use a separate property, jdk.http.auth.proxying.disabledSchemes, and Oracle’s 8u111 change only disabled Basic for tunnels. Since almost every site uses HTTPS today, set the tunneling property anyway.
Does this work in Kotlin, Scala or Spring apps?
Yes. They run on the same JVM and use the same java.net classes, so the Authenticator, the ProxySelector and the tunneling property work the same way. Frameworks that wrap their own HTTP library may need the proxy set in that library’s configuration as well.
Can I use a SOCKS5 proxy in Java?
Yes, with Proxy.Type.SOCKS or the socksProxyHost and socksProxyPort properties. For HTTP clients, the HTTP proxy endpoint is usually simpler.
How do I use several proxy locations at once?
Create one HttpClient per ProxyEmpire username, each with its own authenticator, and set a different location or session for each username in the dashboard.
References
Sources and documentation- Oracle — Java Networking and Proxies. docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
- Oracle — JDK 8u111 release notes (Basic authentication disabled for HTTPS tunneling). oracle.com/java/technologies/javase/8u111-relnotes.html
- Oracle —
java.net.httpmodule summary (networking properties). docs.oracle.com/en/java/javase/21/docs/api/java.net.http/module-summary.html - Oracle —
java.net.Authenticator. docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/Authenticator.html - MDN — 407 Proxy Authentication Required. developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Status/407
Connect your Java app to residential proxies
Test the setup in this guide with the $1.97 trial: 100 MB of residential and 50 MB of mobile bandwidth, HTTP(S) and SOCKS5, targeting down to city and ISP, and 24/7 support from real people.














