Java Proxy Authentication: HttpClient and HttpURLConnection

Developer guide · Last reviewed 27 September 2026 · 10 min read

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 goes

Java 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.

The pieces of a Java proxy setup
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 default

To 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.

Allow Basic proxy authentication for HTTPS tunnels
# 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 client

Since 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:

Java 11+ — HttpClient through an authenticated proxy
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 for RequestorType.PROXY keeps your proxy password from being sent to websites that ask for their own login.
  • Build the client once and reuse it. HttpClient keeps 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 libraries

Plenty 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:

Java — HttpURLConnection with a proxy and credentials
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 change

When 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.

JVM-wide proxy settings
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 code

For 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 dashboard

With 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.

New IP per requestEach request can leave from a different IP. Best for collecting many independent pages.
Sticky sessionKeep one IP until you rotate it or the device behind it goes offline. Best for logins and multi-step flows.
Location targetingCountry, region, city, ZIP, ISP or mobile carrier, and ASN, at no extra charge.
One client per sessionCreate one HttpClient per username when you need several sessions or locations in parallel.

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 seeLikely cause and fix
407 on HTTPS, HTTP worksBasic is disabled for HTTPS tunnels. Start the JVM with -Djdk.http.auth.tunneling.disabledSchemes="".
407 on everythingNo 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 effectIt was set after the first connection. Move it to the command line or the very start of main.
Your own IP shows upThe proxy wasn’t applied to that client or connection. Check the ProxySelector or the openConnection(proxy) call.
Some sites never loadFinancial, 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 answers
How 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
  1. Oracle — Java Networking and Proxies. docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
  2. Oracle — JDK 8u111 release notes (Basic authentication disabled for HTTPS tunneling). oracle.com/java/technologies/javase/8u111-relnotes.html
  3. Oracle — java.net.http module summary (networking properties). docs.oracle.com/en/java/javase/21/docs/api/java.net.http/module-summary.html
  4. Oracle — java.net.Authenticator. docs.oracle.com/en/java/javase/21/docs/api/java.base/java/net/Authenticator.html
  5. 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.

Flexible Pricing Plan

logo purple proxyempire

Our state-of-the-art proxies.

Experience online freedom with our unrivaled web proxy solutions. Pioneering in collecting location specific data at scale, our premium, ethically-sourced network boasts a vast pool of IPs, expansive location choices, high success rate, and versatile pricing. Advance your digital journey with us.

🏘️ Rotating Residential Proxies
  • 30M+ Premium Residential IPs
  •  170+ Countries
    Every residential IP in our network corresponds to an actual desktop device with a precise geographical location. Our residential proxies aare fast and reliable, with 99.9% uptime, and work for a wide range of use cases. You can use Country, Region, City and ISP targeting for our rotating residential proxies.

See our Rotating Residential Proxies

📍 Static Residential Proxies
  • 19 Countries
    Buy a dedicated static residential IP from one of the 19 countries that we offer proxies in. Keep the same IP for a month or longer, while benefiting from their fast speed and stability.

See our Static Residential Proxies

📳 Rotating Mobile Proxies
  • 4M+ Premium Mobile IPs
  •  170+ Countries
    Access millions of clean mobile IPs with precise targeting including Country, Region, City, and Mobile Carrier. Get far fewer IP blocks and CAPTCHAs with our 4G and 5G proxies.

See our Mobile Proxies

📱 Dedicated Mobile Proxies
  • 5+ Countries
  • 50+ Locations
    Get your own dedicated mobile proxy in one of our supported locations, with unlimited bandwidth and unlimited IP changes on demand. A great choice when you need a small number of mobile IPs and a lot of proxy bandwidth.

See our 4G & 5G Proxies

🌐 Rotating Datacenter Proxies
  • 197,000+ IPs Premium IPs
  •  62 Countries
    On a budget and need to do some simple scraping tasks? Our datacenter proxies are the perfect fit! Get started with as little as $2

See our Datacenter Proxies

proxy locations

30M+ rotating IPs

99% uptime - high speed

99.9% uptime.

dedicated support team

24/7 Dedicated Support.

fair price

Fair Pricing.

ProxyEmpire Footer
Follow Us
🏠 Residential Proxies Rotating / Static / Unlimited
📱 Mobile Proxies Rotating and Dedicated
🖥️ Datacenter Proxies Rotating
🌍 Proxy Locations 30M+ Proxies · Worldwide coverage
🏎️ Speed High-speed connections