Java proxy authentication: 407 with the right password

Java turns off password authentication for HTTPS proxy tunnels by default. Your Authenticator is never asked, and a correct login gets a 407. One property fixes it.

5 min readTested on Java 21 and 17
With default settings Java answers a proxy's 407 with no credentials for HTTPS; clearing jdk.http.auth.tunneling.disabledSchemes makes the same login work.Same login, same proxy, one system property apartjava ProxyDemo.javaHttpClient: 407 nullHttpURLConnection: Unable to tunnel through proxy … 407java -Djdk.http.auth.tunneling.disabledSchemes= ProxyDemo.java200 { "country": "DE", … }Java 21 and 17 · October 14, 2026

In short

Start Java with -Djdk.http.auth.tunneling.disabledSchemes= (empty), or set the property to "" in code before the first request. Then a proxy Authenticator works for https:// URLs. Without it, every login gets 407.

-Djdk.http.auth.tunneling.disabledSchemes=statusCode() == 407

For an https:// URL, Java asks the proxy to open a tunnel with CONNECT. The proxy answers 407 and asks for a login with the Basic scheme. Since Java 8u111, Java refuses to send Basic credentials for tunnels: the scheme is listed in the system property jdk.http.auth.tunneling.disabledSchemes. So your Authenticator is never called, and the 407 is final.

We ran the same program twice, once with Java’s defaults and once with the property cleared, against our gateway:

407java Cases.java (HttpClient, correct login)Basic disabled for tunnels
IOException 407java Cases.java (HttpURLConnection, correct login)Unable to tunnel through proxy
200java -Djdk.http.auth.tunneling.disabledSchemes= Cases.javaboth clients

The default exists so that a Basic password never travels in clear text to a proxy without you deciding to allow it. The connection from your program to an HTTP proxy port isn’t encrypted, so clearing the property means trusting the network between you and the proxy. For http:// URLs (no tunnel) Java never disabled Basic; the same login works there.

Every message, reproduced

java.net.http.HttpClient and HttpURLConnection on Java 21, against via.proxies.vision:9000.

SettingLoginHttpClientHttpURLConnection
defaultcorrectresponse 407, body nullIOException: Unable to tunnel through proxy. Proxy returns "HTTP/1.1 407 …"
property clearedcorrect200200
property clearedwrong passwordIOException: too many authentication attempts. Limit: 3—
property cleared-country-usaIOException: Tunnel failed, got: 403—
property clearedChicago in GermanyIOException: Tunnel failed, got: 465—
Fig. 1 — Java 21.0.12, October 14, 2026. The proxy statuses 403 and 465 are our gateway’s; see below.

HttpClient doesn’t throw on 407

With the defaults, client.send() returned normally with statusCode() == 407 and an empty body. Code that only catches exceptions will happily parse nothing. Check the status before reading the body.

A real wrong password

Once Java may send the login, a wrong one gives too many authentication attempts. Limit: 3: Java asked your Authenticator three times and got the same rejected answer. That message, not the plain 407, means the credentials are wrong.

403: a value in the username

Targeting goes into the proxy username. Our gateway refuses values it can’t read, such as a three-letter country code or a session label on the rotating port: Tunnel failed, got: 403. A key it doesn’t know, such as -cc-DE, is ignored without an error.

465: no exit

The login and the username are fine, but no residential exit matches every filter right now. Drop the narrowest one, usually the city. The same codes in other clients: curl, Python requests.

A setup that works

HttpClient with a proxy Authenticator, the property cleared in code. Ran unchanged on Java 21 and 17 with java ProxyDemo.java.

ProxyDemo.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 {
        // Java refuses Basic proxy auth for HTTPS tunnels unless this is cleared,
        // before the first request. (Or start java with -Djdk.http.auth.tunneling.disabledSchemes=)
        System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");

        String user = System.getenv("PROXY_USER") + "-country-de";
        String pass = System.getenv("PROXY_PASS");

        HttpClient client = HttpClient.newBuilder()
            .proxy(ProxySelector.of(new InetSocketAddress("via.proxies.vision", 9000)))
            .authenticator(new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    // Answer the proxy only, never a website asking for a login.
                    return getRequestorType() == RequestorType.PROXY
                        ? new PasswordAuthentication(user, pass.toCharArray())
                        : null;
                }
            })
            .connectTimeout(Duration.ofSeconds(30))
            .build();

        HttpRequest request = HttpRequest.newBuilder(URI.create("https://ipinfo.io/json")).build();
        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        // A 407 here is the proxy's answer, not the website's: check the login.
        System.out.println(response.statusCode() + " " + response.body());
    }
}
txt
200 {
  "ip": "94.134.182.246",
  "city": "Karlsruhe",
  "country": "DE",
  …

Each new connection through port 9000 gets a new exit. For the same IP across connections, put a session label into the username and use port 10000: USER-country-de-session-job1-lifetime-10.

Why does Java get 407 from the proxy when the password is correct?
Since Java 8u111 the property jdk.http.auth.tunneling.disabledSchemes contains Basic, so Java won't send a username and password with Basic auth when it opens an HTTPS tunnel through a proxy. Your Authenticator is never used and the proxy answers 407. Set the property to an empty string.
How do I set jdk.http.auth.tunneling.disabledSchemes?
On the command line: java -Djdk.http.auth.tunneling.disabledSchemes= … (with nothing after the equals sign). Or in code with System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "") before the first request. Both worked on Java 21 and 17 in our test.
Why does HttpClient return 407 instead of throwing?
java.net.http.HttpClient hands the proxy's 407 back as an ordinary response: statusCode() is 407 and the body is empty. HttpURLConnection throws 'Unable to tunnel through proxy. Proxy returns HTTP/1.1 407'. Check the status code before you read the body.
What does 'too many authentication attempts. Limit: 3' mean?
Java sent your login, the proxy rejected it, and Java tried three times. Unlike the plain 407, this one really is a wrong username or password.

Residential proxies, pay per GB

HTTP and SOCKS5. Country, city and sticky sessions in the username. No subscription.