All guides
DEVELOPER GUIDES · 5 MIN READ

How to set a proxy for Java HttpClient

Use ProxySelector with Java HttpClient, set timeouts, and check the exit IP from the same client. Includes a runnable example.

How to set a proxy for Java HttpClient

Set HttpClient.Builder.proxy with ProxySelector.of to route a Java HttpClient through one explicit HTTP proxy. Use the same client to request the target and an IP-check endpoint, then validate both responses; a rotating proxy can return a different exit IP for each request.

Audience: Java developers familiar with compiling and running a small Java SE program.

Warning: These requests consume proxy traffic and may incur charges; a body-size check cannot refund bytes already transferred. Proxy credentials can be exposed on an unencrypted client-to-proxy link.

Start with a sandbox or test account and a small provider budget. This example uses an HTTP proxy, so send credentials only over a trusted client-to-proxy network. Remove any credential binding that reaches logs.

Check prerequisites

Tested with Eclipse Temurin JDK 21.0.9. Install Temurin from the official release downloads. This example uses an HTTP proxy with host and port bindings; it does not configure SOCKS proxies.

HTTP Basic authentication for an HTTPS CONNECT tunnel can be disabled by Java's jdk.http.auth.tunneling.disabledSchemes security property. A java.net.Authenticator callback alone does not prove that the JDK will send credentials.

Use a proxy authentication scheme supported by the runtime policy, then verify the negotiated route separately.

Build and run one client

Set PROXY_HOST and PROXY_PORT through the runtime environment. For a local shell, set the non-secret values with export PROXY_HOST=proxy.example.net PROXY_PORT=8080. When proxy authentication is required, bind PROXY_USER and PROXY_PASSWORD from a secret store; the code scopes the authenticator to that proxy endpoint. Save as ProxyGet.java:

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.Authenticator;
import java.net.InetAddress;
import java.net.PasswordAuthentication;
import java.net.InetSocketAddress;
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.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public final class ProxyGet {
    private static final String TARGET_URL = "https://example.com/";
    private static final String EXPECTED_MARKER = "Example Domain";
    private static final String EXIT_CHECK_URL = "https://api.ipify.org?format=json";
    private static final Duration CONNECT_TIMEOUT = Duration.ofSeconds(5);
    private static final Duration REQUEST_TIMEOUT = Duration.ofSeconds(15);
    private static final int MAX_BYTES = 1_048_576;

    private static String validatedIp(String text) throws IOException {
        java.util.regex.Matcher matcher = java.util.regex.Pattern
            .compile("\\s*\\{\\s*\"ip\"\\s*:\\s*\"([0-9a-fA-F:.]+)\"\\s*}\\s*")
            .matcher(text);
        if (!matcher.matches()) {
            throw new IOException("IP-check response did not contain an address");
        }
        String candidate = matcher.group(1);
        try {
            if (candidate.indexOf(':') >= 0) {
                byte[] address = InetAddress.getByName(candidate).getAddress();
                if (address.length != 16) {
                    throw new IOException("IP-check response did not contain a valid IP address");
                }
                return InetAddress.getByAddress(address).getHostAddress();
            }
            String[] octets = candidate.split("\\.", -1);
            if (octets.length != 4) {
                throw new IOException("IP-check response did not contain a valid IP address");
            }
            for (String octet : octets) {
                if (octet.isEmpty() || (octet.length() > 1 && octet.charAt(0) == '0')) {
                    throw new IOException("IP-check response did not contain a valid IP address");
                }
                int value = Integer.parseInt(octet);
                if (value > 255) {
                    throw new IOException("IP-check response did not contain a valid IP address");
                }
            }
            return candidate;
        } catch (java.net.UnknownHostException | NumberFormatException exception) {
            throw new IOException("IP-check response did not contain a valid IP address");
        }
    }

    private static byte[] readBody(InputStream input) throws IOException {
        ExecutorService readerExecutor = Executors.newSingleThreadExecutor();
        Future<byte[]> body = readerExecutor.submit(() -> {
            try (InputStream stream = input; ByteArrayOutputStream output = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[8192];
                int total = 0;
                int count;
                while ((count = stream.read(buffer)) != -1) {
                    if (total + count > MAX_BYTES) {
                        throw new IOException("Response exceeded the 1 MiB limit");
                    }
                    output.write(buffer, 0, count);
                    total += count;
                }
                return output.toByteArray();
            }
        });
        try {
            return body.get(10, TimeUnit.SECONDS);
        } catch (TimeoutException exception) {
            try {
                input.close();
            } catch (IOException ignored) {
                // The response is rejected because its body did not finish in time.
            }
            body.cancel(true);
            throw new IOException("Response body exceeded the 10-second read deadline");
        } catch (InterruptedException exception) {
            Thread.currentThread().interrupt();
            throw new IOException("Response body read was interrupted");
        } catch (ExecutionException exception) {
            Throwable cause = exception.getCause();
            if (cause instanceof IOException ioException) {
                throw ioException;
            }
            throw new IOException("Response body read failed");
        } finally {
            readerExecutor.shutdownNow();
        }
    }

    public static void main(String[] args) throws Exception {
        String host = System.getenv("PROXY_HOST");
        String portValue = System.getenv("PROXY_PORT");
        String user = System.getenv("PROXY_USER");
        String password = System.getenv("PROXY_PASSWORD");
        if (host == null || host.isBlank() || portValue == null
            || ((user == null) != (password == null))) {
            throw new IllegalArgumentException("Set a valid proxy host, port, and paired credentials");
        }
        int port = Integer.parseInt(portValue);
        if (port < 1 || port > 65535) {
            throw new IllegalArgumentException("PROXY_PORT is outside the TCP port range");
        }
        HttpClient.Builder builder = HttpClient.newBuilder()
            .proxy(ProxySelector.of(new InetSocketAddress(host, port)))
            .connectTimeout(CONNECT_TIMEOUT)
            .followRedirects(HttpClient.Redirect.NEVER);
        if (user != null) {
            builder.authenticator(new Authenticator() {
                @Override
                protected PasswordAuthentication getPasswordAuthentication() {
                    if (getRequestorType() != RequestorType.PROXY
                        || !host.equalsIgnoreCase(getRequestingHost())
                        || port != getRequestingPort()) {
                        return null;
                    }
                    return new PasswordAuthentication(user, password.toCharArray());
                }
            });
        }
        HttpClient client = builder.build();

        for (String url : new String[] {TARGET_URL, EXIT_CHECK_URL}) {
            HttpRequest request = HttpRequest.newBuilder(URI.create(url))
                .timeout(REQUEST_TIMEOUT)
                .GET()
                .build();
            HttpResponse<InputStream> response;
            try {
                response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
            } catch (IOException exception) {
                throw new IllegalStateException("Request failed during transport or proxy negotiation");
            } catch (InterruptedException exception) {
                Thread.currentThread().interrupt();
                throw new IllegalStateException("Request was interrupted");
            }
            if (response.statusCode() == 407) {
                if (response.body() != null) {
                    response.body().close();
                }
                throw new IllegalStateException("Proxy authentication or CONNECT returned HTTP 407");
            }
            if (response.statusCode() != 200) {
                if (response.body() != null) {
                    response.body().close();
                }
                throw new IllegalStateException("Target returned a non-accepted HTTP status");
            }
            byte[] body = readBody(response.body());
            String text = new String(body, java.nio.charset.StandardCharsets.UTF_8);
            int bodyBytes = body.length;
            if (url.equals(TARGET_URL) && !text.contains(EXPECTED_MARKER)) {
                throw new IllegalStateException("Target marker was absent");
            }
            String exitIp = null;
            if (url.equals(EXIT_CHECK_URL)) {
                exitIp = validatedIp(text);
            }
            System.out.println("status=" + response.statusCode() + " body_bytes=" + bodyBytes);
            if (exitIp != null) {
                System.out.println("exit_ip=" + exitIp);
            }
        }
    }
}

Compile and run:

javac ProxyGet.java
java ProxyGet

On success the client makes two application GET requests and follows no redirects; proxy authentication can add protocol exchanges. It sets a 5-second connection timeout, a 15-second request timeout, a 10-second response-body deadline, and a 1 MiB body cap.

The body cap limits application memory, not provider-billed traffic.

Illustrative output. The IP is replaced with a documentation address; body byte counts can vary:

status=200 body_bytes=559
status=200 body_bytes=23
exit_ip=203.0.113.10

Interpret the route check

Observation Likely boundary Next check
HTTP 407 Proxy authentication or CONNECT Check the proxy scheme and supported authentication policy; do not resend rejected credentials
Authenticator is called for a non-proxy requestor Credential scope Return no credentials for that requestor and check which hop challenged
Request fails before a response Client, proxy, or network Check host, port, TLS trust, and proxy policy
HTTP 401 or 403 Target policy Check target-side authorization
HTTP 502, 503, or 504 Gateway or target availability Defer the operation; retry only under an explicit bounded policy that honors Retry-After
HTTP 200 without the marker Application validation Reject the response and inspect its content
Body exceeds 1 MiB or read deadline Local resource policy Reject it; change bounds only for an authorized use case

The IP-check response shows the address seen by that service for its request. It does not establish geographic accuracy, anonymity, or a stable exit across rotating requests. A 407 can occur during proxy authentication or CONNECT negotiation; a target 401 or 403 occurs after the target responds. See the HTTP 407 guide for the proxy-side failure boundary.

Sources and further reading

ProxyLaneTraffic never expires

Premium residential proxies
From $2/GB

Scale your workflows with the tools you already use. Rotating or sticky sessions up to 72 hours.

Try 350 MB for $1.95 View plans Proxies for web scraping

Keep reading

Amazon Scraper API vs Proxy: Choose a Product Data Source

Compare seller APIs, licensed Amazon product data and proxy page checks by record, access scope and reuse rights.

Read guide

Antidetect browser proxy setup: choose a profile guide

Choose the right anti-detect browser proxy guide, map the shared verification steps, and keep account access separate from the network route.

Read guide

Best Proxies for Market Research: Choose by Sample and Cost

Our picks for market research: ProxyLane for small, intermittent projects, plus options for regional tracking, monthly collection and managed extraction.

Read guide