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());
}
}
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();
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());
}
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();
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();
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();
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.