Guide

How to Use Proxies With OkHttp (Java, Kotlin, Android)

How to route OkHttp through a proxy: the proxy() builder method, proxyAuthenticator for auth, SOCKS, a ProxySelector for rotation, per-client design, timeouts, and verifying the exit.

HProxy Team··4 min read
HProxy.Guide

Skip the dead lists.

Our free proxy list re-checks every exit every few minutes across 100+ countries, with a live last-checked time, so you copy IPs that worked moments ago, not a stale text dump.

Open the free proxy list

OkHttp is the HTTP client most Java, Kotlin, and Android code reaches for, and its proxy support is clean once you know that the proxy belongs to the client and authentication goes through a callback rather than a header. This guide covers proxies with OkHttp end to end: the proxy() builder method, authenticating with a proxyAuthenticator, SOCKS, rotating a pool with a ProxySelector, why the proxy is per client and what that means for design, timeouts, and verifying the exit IP.

We run a proxy network and a live proxy checker, so the OkHttp proxy mistake we see most is a Proxy-Authorization header set by hand on the request, which OkHttp does not carry through the CONNECT tunnel the way people expect. The fix is the proxyAuthenticator below. Every example is Java and translates directly to Kotlin. Where one needs a live proxy, pull a fresh one from our free proxy API, which returns recently checked endpoints with no key.

How do you set a proxy in OkHttp?

Build a Proxy and pass it to the client builder. Every request that client sends then goes through it:

import okhttp3.*;
import java.net.InetSocketAddress;
import java.net.Proxy;

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

Request request = new Request.Builder().url("https://httpbin.org/ip").build();
try (Response response = client.newCall(request).execute()) {
    System.out.println(response.body().string());
}

The proxy is a property of the client, not the request. That is the single most important thing to internalise: to change the exit you change the client, or you install a selector that changes it for you.

Authentication goes through a callback

A paid proxy needs credentials, and this is where hand-written headers fail. OkHttp answers a proxy's 407 challenge through a proxyAuthenticator, which it calls with the failed response so you can return a request carrying the right header:

import okhttp3.Credentials;

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("203.0.113.7", 8080)))
    .proxyAuthenticator((route, response) -> {
        String credential = Credentials.basic("myuser", "mypass");
        return response.request().newBuilder()
            .header("Proxy-Authorization", credential)
            .build();
    })
    .build();

Credentials.basic builds the Basic auth value for you. The reason this works where a manual header does not is the CONNECT tunnel: for an HTTPS target, OkHttp authenticates to the proxy during the tunnel setup, and only the proxyAuthenticator path feeds credentials into that step.

OkHttp proxy auth: the callback, not a header
  1. Request via proxy

    HTTPS needs a CONNECT tunnel

  2. Proxy returns 407

    authentication required

  3. proxyAuthenticator fires

    you add Proxy-Authorization

  4. Tunnel opens

    request proceeds

Source: Why a hand-set header fails and the authenticator works

SOCKS proxies

For a SOCKS proxy, change the Proxy.Type to SOCKS:

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("203.0.113.7", 1080)))
    .build();

One caveat carried over from the JVM: SOCKS support resolves the target hostname on your machine by default, so the DNS lookup does not travel to the proxy the way socks5h sends it. If proxy-side resolution matters for your job, either resolve through the proxy explicitly or use an HTTP proxy, which sidesteps the question. We cover the split in what is a SOCKS5 proxy.

Rotating a pool with a ProxySelector

Building a client per proxy works, but it throws away OkHttp's connection pool each time. To rotate through one reused client, install a ProxySelector that returns a different proxy per request:

import java.net.*;
import java.util.*;

class RotatingSelector extends ProxySelector {
    private final List<Proxy> pool;
    private final Random rnd = new Random();

    RotatingSelector(List<Proxy> pool) { this.pool = pool; }

    @Override public List<Proxy> select(URI uri) {
        return List.of(pool.get(rnd.nextInt(pool.size())));
    }
    @Override public void connectFailed(URI uri, SocketAddress sa, java.io.IOException e) {
        // a dead exit surfaces here; a real pool would retire it
    }
}

List<Proxy> pool = List.of(
    new Proxy(Proxy.Type.HTTP, new InetSocketAddress("203.0.113.7", 8080)),
    new Proxy(Proxy.Type.HTTP, new InetSocketAddress("203.0.113.24", 3128))
);

OkHttpClient client = new OkHttpClient.Builder()
    .proxySelector(new RotatingSelector(pool))
    .build();

Pair it with call-level retries so a request that draws a dead exit is retried on another, because on any pool some exits are always down. You can fill the pool from our free proxy API, which returns a plain-text list you can parse into Proxy objects at startup.

Timeouts

An OkHttp client with no timeouts will wait a long time on a proxy that connects and then stalls. Set them on the builder:

OkHttpClient client = new OkHttpClient.Builder()
    .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("203.0.113.7", 8080)))
    .connectTimeout(java.time.Duration.ofSeconds(5))
    .readTimeout(java.time.Duration.ofSeconds(20))
    .callTimeout(java.time.Duration.ofSeconds(30))
    .build();

connectTimeout bounds reaching the proxy, readTimeout bounds waiting for data once connected, and callTimeout caps the whole call including retries and redirects. For unattended scraping, set all three.

Verify the exit IP

Confirm the proxy took effect rather than trusting it. Fetch an IP echo through the proxied client and compare it to your own address:

Request check = new Request.Builder().url("https://httpbin.org/ip").build();
try (Response r = client.newCall(check).execute()) {
    System.out.println("exit IP: " + r.body().string());
}

If the address is your own, the client you sent with has no proxy, or a system ProxySelector overrode it. For a fuller read of the exit, our proxy checker reports the exit IP, country, latency and anonymity grade in one paste.

Where to go from here

OkHttp's proxy model is two rules: the proxy lives on the client, and authentication goes through the proxyAuthenticator callback rather than a header. Once those are habit, the rest is the discipline every proxied client needs: verify each exit, bound your timeouts, and rotate a pool that is alive.

The Java proxy guide covers the java.net and HttpClient paths if part of your code is not on OkHttp, proxies for web scraping covers choosing the right proxy type, and how to fix 407 Proxy Authentication Required goes deeper on the auth error above. When a hand-rolled ProxySelector becomes a chore, a rotating gateway that returns a fresh residential IP per request at $0.44/GB lets one client keep a single endpoint while the exit changes underneath it.

Frequently asked questions

How do I set a proxy in OkHttp?
Build a Proxy and pass it to the client builder: new OkHttpClient.Builder().proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, port))).build(). The proxy is set on the client, not on individual requests, so every call that client makes uses it. Use Proxy.Type.SOCKS instead of HTTP for a SOCKS proxy.
How do I authenticate an OkHttp proxy?
Set a proxyAuthenticator on the builder, not an Authorization header on the request. OkHttp calls it when the proxy returns 407 and you return a request carrying the Proxy-Authorization header, usually built with Credentials.basic(user, pass). Setting proxyAuthenticator rather than a plain header is what makes CONNECT tunnels to HTTPS targets authenticate correctly.
How do I rotate proxies in OkHttp?
Because the proxy is fixed on the client, rotation means either building one client per proxy and picking among them, or installing a ProxySelector that returns a different proxy per request. A ProxySelector lets one reused client change exits without rebuilding, which keeps the connection pool and settings while varying the IP.
Does OkHttp support SOCKS5 proxies?
Yes. Pass Proxy.Type.SOCKS with the SOCKS server address. One limit to know: Java's SOCKS support resolves the hostname locally by default, so for proxy-side DNS (the socks5h behaviour) you either resolve through the proxy explicitly or front it with a small local bridge. For most scraping an HTTP proxy avoids that question entirely.
Why is my OkHttp proxy being ignored?
The usual causes are setting a Proxy-Authorization header on the request instead of a proxyAuthenticator (OkHttp strips or mishandles it on CONNECT), reusing a default client that has no proxy set, or a system proxy selector overriding you. Set the proxy and the proxyAuthenticator on the specific client you send with, and verify the exit IP to confirm.

Get proxies that are alive right now

Our free list re-checks every exit every few minutes and shows a last-checked time, so you copy IPs that worked moments ago, not a stale text dump. When the location has to survive a real check, the paid network holds up.

129M+ proxy checks run · 100+ countries · HTTP / HTTPS / SOCKS · re-checked every few minutes · no signup