NewUnlimited residential proxies: unmetered traffic on a fixed price. From $33.25/day

Java ยท Integration guide

How to use proxies with Java

The HttpClient built into Java 11 and later supports authenticated proxies once one JDK default is changed. This guide covers that setting, sticky sessions, location targeting and retries for gateway errors.

  • 5 steps
  • 10 min setup
  • Copy-paste code

Before you start

  • Java 11 or newer (java.net.http.HttpClient)
  • No external libraries
  • A Proxonym residential or mobile plan
Get proxy credentials

Already a customer? Your username and password are in the dashboard.

  1. Check your JDK

    HttpClient ships with Java 11 and later. Java 11 can also run a single source file directly, which is how the examples below are launched.

    Shell
    java -version
  2. Allow Basic authentication on proxy tunnels

    Since Java 8u111 the JDK disables Basic authentication for HTTPS tunnels by default, so the gateway login fails with a 407. Clear the jdk.http.auth.tunneling.disabledSchemes property on the command line, or in code before the first HttpClient is created.

    Shell
    java -Djdk.http.auth.tunneling.disabledSchemes="" ProxyCheck.java
  3. Send a request through the gateway

    Point the client at the gateway with a ProxySelector and answer proxy login requests with an Authenticator. The program prints the exit IP the target sees.

    Java
    // ProxyCheck.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 ProxyCheck {
        public static void main(String[] args) throws Exception {
            // Must run before the first HttpClient is built
            System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
    
            HttpClient client = HttpClient.newBuilder()
                    .proxy(ProxySelector.of(new InetSocketAddress("gw.proxonym.com", 8000)))
                    .authenticator(new Authenticator() {
                        @Override
                        protected PasswordAuthentication getPasswordAuthentication() {
                            if (getRequestorType() == RequestorType.PROXY) {
                                return new PasswordAuthentication("USERNAME", "PASSWORD".toCharArray());
                            }
                            return null;
                        }
                    })
                    .connectTimeout(Duration.ofSeconds(10))
                    .build();
    
            HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.ipify.org"))
                    .timeout(Duration.ofSeconds(60))
                    .build();
    
            HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
            System.out.println("Exit IP: " + response.body());
        }
    }
  4. Rotate, hold or target an IP

    Each HttpClient keeps its own connections and login, and the gateway assigns an IP per connection. Use a new client for a new IP, one client per -session- ID for a sticky IP of up to 120 minutes, and location parameters for targeting.

    Java
    // Sessions.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;
    import java.util.UUID;
    
    public class Sessions {
        static HttpClient clientFor(String username) {
            return HttpClient.newBuilder()
                    .proxy(ProxySelector.of(new InetSocketAddress("gw.proxonym.com", 8000)))
                    .authenticator(new Authenticator() {
                        @Override
                        protected PasswordAuthentication getPasswordAuthentication() {
                            return getRequestorType() == RequestorType.PROXY
                                    ? new PasswordAuthentication(username, "PASSWORD".toCharArray())
                                    : null;
                        }
                    })
                    .connectTimeout(Duration.ofSeconds(10))
                    .build();
        }
    
        static String exitIp(HttpClient client) throws Exception {
            HttpRequest request = HttpRequest.newBuilder(URI.create("https://api.ipify.org"))
                    .timeout(Duration.ofSeconds(60))
                    .build();
            return client.send(request, HttpResponse.BodyHandlers.ofString()).body();
        }
    
        public static void main(String[] args) throws Exception {
            System.setProperty("jdk.http.auth.tunneling.disabledSchemes", "");
    
            // Rotating: a new client opens a new connection, so each request gets a new IP
            for (int i = 0; i < 3; i++) {
                System.out.println("rotating " + exitIp(clientFor("USERNAME-country-us")));
            }
    
            // Sticky: one client and one session ID keep the same IP for up to 30 minutes
            String session = UUID.randomUUID().toString().replace("-", "").substring(0, 8);
            HttpClient sticky = clientFor("USERNAME-country-us-session-" + session + "-lifetime-30");
            for (int i = 0; i < 3; i++) {
                System.out.println("sticky " + exitIp(sticky));
            }
    
            // Geo-targeting: London, United Kingdom
            System.out.println("london " + exitIp(clientFor("USERNAME-country-gb-city-london")));
        }
    }
  5. Retry gateway errors

    A refused tunnel throws an IOException such as Tunnel failed, got: 502, and a rejected login ends with too many authentication attempts. Retry 429, 502 and 504 with a short backoff, and stop on 402, 403 or a failed login, which retries cannot fix.

    Java
    static HttpResponse<String> sendWithRetry(HttpClient client, HttpRequest request, int tries)
            throws IOException, InterruptedException {
        for (int attempt = 1; ; attempt++) {
            try {
                HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
                int status = response.statusCode();
                if ((status != 429 && status < 500) || attempt == tries) {
                    return response;
                }
            } catch (IOException e) {
                String message = String.valueOf(e.getMessage());
                System.err.println("attempt " + attempt + ": " + message);
                // 402 (balance), 403 (blocked target) and a rejected login will not go away on a retry
                boolean permanent = message.matches(".*got: 40[23].*") || message.contains("authentication");
                if (permanent || attempt == tries) {
                    throw e;
                }
            }
            Thread.sleep(1000L * attempt);
        }
    }

Frequently asked questions

Why does Java get a 407 even with the right credentials?

The JDK blocks Basic authentication on HTTPS tunnels by default, so the Authenticator is never used for the CONNECT request. Set jdk.http.auth.tunneling.disabledSchemes to an empty value before the first HttpClient is created, and make sure your Authenticator returns credentials when the requestor type is PROXY.

Can I use OkHttp instead of HttpClient?

Yes. Give the OkHttpClient builder a java.net.Proxy of type HTTP pointing at gw.proxonym.com:8000, and set a proxyAuthenticator that adds a Proxy-Authorization header built with Credentials.basic. OkHttp handles CONNECT authentication itself, so the JDK tunneling property does not apply, and the username parameters work the same way.

How do I get a new IP on every request?

The gateway assigns an IP to each new connection, and an HttpClient keeps its connections open for reuse. Build a fresh client for each request when every call must exit from a different IP, or give each request its own random session ID. Reuse one client when you want a stable, sticky IP.

What does Tunnel failed, got: 502 mean?

HttpClient reports a refused CONNECT request this way, with the gateway status at the end. A 502 means the upstream peer failed and a 504 that the target timed out, and both usually succeed on a retry. A 402 points to an empty balance or expired plan, and a 403 to a target blocked by the acceptable use policy.

Ready to plug it in? Get your credentials.

Pick a plan, copy your username and password from the dashboard and run the code above.

  • Pay as you go
  • Instant activation
  • Crypto accepted
  • 24/7 support