DEV Community

Cover image for Proxies in Java: Setting Up HttpClient and OkHttp
ProxiesThatWork
ProxiesThatWork

Posted on

Proxies in Java: Setting Up HttpClient and OkHttp

If you need to route Java HTTP requests through a proxy, you have two solid options: the built-in java.net.http.HttpClient (Java 11+, no dependencies), or OkHttp, if you're already using it for other reasons.

Both handle proxies natively. Here's the direct answer for each, starting with the one that needs zero extra dependencies.

Setting a proxy with java.net.http.HttpClient

Since Java 11, the standard library ships its own HTTP client. To send requests through a proxy, give it a ProxySelector pointed at your proxy's host and port:

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;

public class ProxyExample {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newBuilder()
            .proxy(ProxySelector.of(new InetSocketAddress("proxy-host", 8080)))
            .build();

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://example.com"))
            .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
        System.out.println(response.body());
    }
}
Enter fullscreen mode Exit fullscreen mode

Every request sent with client now goes through the proxy. No third-party library required.

Adding authentication to HttpClient

Most paid proxies need a username and password. HttpClient doesn't read credentials from a URL, you give it an Authenticator instead:

import java.net.Authenticator;
import java.net.PasswordAuthentication;

HttpClient client = HttpClient.newBuilder()
    .proxy(ProxySelector.of(new InetSocketAddress("proxy-host", 8080)))
    .authenticator(new Authenticator() {
        @Override
        protected PasswordAuthentication getPasswordAuthentication() {
            return new PasswordAuthentication("username", "password".toCharArray());
        }
    })
    .build();
Enter fullscreen mode Exit fullscreen mode

HttpClient calls this automatically when the proxy responds with a 407 Proxy Authentication Required, and retries the request with the credentials attached.

Setting a proxy with OkHttp

If your project already pulls in OkHttp, the setup is just as direct:

import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

import java.net.InetSocketAddress;
import java.net.Proxy;

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy-host", 8080)))
    .build();

Request request = new Request.Builder()
    .url("https://example.com")
    .build();

try (Response response = client.newCall(request).execute()) {
    System.out.println(response.body().string());
}
Enter fullscreen mode Exit fullscreen mode

Same idea: build a Proxy object, attach it to the client, reuse that client for every call.

Adding authentication to OkHttp

OkHttp uses an Authenticator too, but its own interface, not java.net.Authenticator. This one responds to the 407 challenge and returns a modified request with the auth header attached:

import okhttp3.Credentials;
import okhttp3.Route;

import java.io.IOException;

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy-host", 8080)))
    .proxyAuthenticator((Route route, Response response) -> {
        String credential = Credentials.basic("username", "password");
        return response.request().newBuilder()
            .header("Proxy-Authorization", credential)
            .build();
    })
    .build();
Enter fullscreen mode Exit fullscreen mode

Credentials.basic() handles the Base64 encoding for you, you don't need to build the header string by hand.

Confirming the proxy is actually being used

A 200 OK response isn't proof the proxy is in the request path. If the proxy config was silently ignored, you'd still get a successful response, just from your own IP instead of the proxy's.

Hit an IP-echo endpoint with your proxied client and check what comes back:

HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.ipify.org"))
    .build();
Enter fullscreen mode Exit fullscreen mode

If the returned IP matches your proxy provider's range, it's working. If it matches your own connection's IP, the proxy isn't actually being applied, usually because a request was sent with a plain HttpClient.newHttpClient() instead of the builder you configured.

Handling the errors you'll actually hit

407 Proxy Authentication Required
Your authenticator either isn't attached to the client, or the credentials are wrong. With HttpClient, double-check .authenticator(...) was called on the same builder that made the client you're using, it's easy to configure one client and accidentally send requests with another.

Connection refused / connection timed out
The proxy host or port is wrong, or the proxy is down. Set an explicit timeout so a dead proxy fails fast instead of hanging:

HttpClient client = HttpClient.newBuilder()
    .proxy(ProxySelector.of(new InetSocketAddress("proxy-host", 8080)))
    .connectTimeout(java.time.Duration.ofSeconds(10))
    .build();
Enter fullscreen mode Exit fullscreen mode

OkHttp's builder has the equivalent: .connectTimeout(10, TimeUnit.SECONDS).

ProxySelector.of() silently not applying to HTTPS requests
This one catches people off guard: ProxySelector.of() covers both HTTP and HTTPS targets by default, but if you've built a custom ProxySelector subclass instead, make sure select() returns your proxy for HTTPS URIs too, not just plain HTTP ones. A selector that only handles http:// targets will silently connect directly for every https:// request.

Which one should you use?

If you're not already using OkHttp for something else, HttpClient is the simpler choice, it ships with the JDK, so there's nothing to add to your build file. If your project already depends on OkHttp (common in Android and Spring-adjacent stacks), stick with it rather than running two HTTP clients side by side.

Either way, the pattern is the same: configure the proxy once on the client, reuse that client everywhere, and set a timeout so a bad proxy fails loudly instead of hanging your app. It's the same shape as setting up a proxy client in Go, build the client once with the proxy attached, then reuse it for every request, just expressed through Java's builder pattern instead of a Go struct.

These libraries handle the network connection, not browser fingerprinting or JavaScript rendering. If a target site is blocking on IP reputation rather than bot-detection scripts, a plain datacenter proxy pool is usually enough for either client above, without paying for residential pricing you don't need.

Top comments (0)