Java HttpClient Proxy Setup: Authenticator and 407 Fixes
Set up Java HttpClient with ProxySelector and a proxy-only Authenticator, fix the HTTPS 407 from disabled Basic tunnelling, compare HttpURLConnection and OkHttp, and verify the exit.
gw.databay.co:8888Operating principle
Route through one gateway selector, answer only the proxy challenge, and allow Basic for CONNECT deliberately before trusting an HTTPS result.
Build a Java HttpClient That Routes Through the Gateway
java.net.http.HttpClient (Java 11 and later) keeps routing and credentials apart: a ProxySelector picks the hop and an Authenticator answers challenges, the pairing shown in Oracle's HttpClient reference. The program below reads credentials injected by your secret store, releases them only to the gateway, bounds both timeouts and checks the exit before any real destination.
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 DatabayProxyCheck {
static final String PROXY_HOST = "gw.databay.co";
static final int PROXY_PORT = 8888;
public static void main(String[] args) throws Exception {
String user = System.getenv("DATABAY_PROXY_USER");
String pass = System.getenv("DATABAY_PROXY_PASS");
if (user == null || pass == null) {
throw new IllegalStateException("Missing DATABAY_PROXY_USER or DATABAY_PROXY_PASS");
}
InetSocketAddress gateway = new InetSocketAddress(PROXY_HOST, PROXY_PORT);
if (gateway.isUnresolved()) {
throw new IllegalStateException("Cannot resolve " + PROXY_HOST);
}
Authenticator proxyOnly = new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
boolean gatewayChallenge = getRequestorType() == RequestorType.PROXY
&& PROXY_HOST.equalsIgnoreCase(getRequestingHost())
&& getRequestingPort() == PROXY_PORT;
// Never hand proxy credentials to a destination server.
return gatewayChallenge ? new PasswordAuthentication(user, pass.toCharArray()) : null;
}
};
HttpClient client = HttpClient.newBuilder()
.proxy(ProxySelector.of(gateway))
.authenticator(proxyOnly)
.connectTimeout(Duration.ofSeconds(10))
.build();
HttpRequest check = HttpRequest.newBuilder(URI.create("https://api.ipify.org?format=json"))
.timeout(Duration.ofSeconds(30))
.build();
HttpResponse<String> response = client.send(check, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() != 200) {
// 407 here: Basic still disabled for CONNECT (next section).
throw new IllegalStateException("Exit check returned HTTP " + response.statusCode());
}
System.out.println("Observed exit: " + response.body());
}
}Run it as a single-file program on Java 17 or later; the next section explains the property.
# DATABAY_PROXY_USER and DATABAY_PROXY_PASS are injected by the secret store
java -Djdk.http.auth.tunneling.disabledSchemes="" DatabayProxyCheck.javaconnectTimeout applies only when a new connection must be opened, and the request timeout bounds the wait for a response; without it, send can block indefinitely. Use this setup only for destinations you own or are permitted to access, and follow each destination's terms. Keep both variables in the secret manager and out of logs, tickets and screenshots. HttpClient implements AutoCloseable only from Java 21, so the example does not close it.
Ready to run this for real?
Credentials appear in your dashboard in minutes, on the same gateway and username format this Java HttpClient guide uses.
Why HTTPS URLs Return 407: Basic Is Disabled for CONNECT
Plain http:// requests are forwarded by the gateway, and the client answers the 407 with Basic credentials. An https:// URL first needs a CONNECT tunnel, which has its own rule: the java.net.http module documentation defines jdk.http.auth.tunneling.disabledSchemes as the schemes the client may not use for CONNECT, defaulting to the runtime's conf/net.properties, which lists Basic. The client's built-in authentication is Basic only, so every HTTPS URL gets a 407, which OpenJDK returns as the response status rather than an exception.
Oracle's Java 8u111 release note, which introduced the default, documents the fix: remove Basic from the property, or set a system property of the same name to an empty value on the command line. Prefer the command line, which changes one application; editing conf/net.properties changes every program on that runtime.
- Set it at launch. OpenJDK reads the value once, when its HTTP classes initialize, so a late
System.setPropertymay have no effect. In PowerShell, quote the whole argument:"-Djdk.http.auth.tunneling.disabledSchemes=". - Know the exposure. The
net.propertiescomment explains the restriction: Basic amounts to cleartext transmission of the password, here on the hop between the JVM andgw.databay.co. Destination TLS still runs end to end inside the tunnel, so accept this only on a network path you trust.
Map Databay Values to Java Networking Controls
Java owns the hop and the credential callback; the Databay username owns pool, location and session. Copy the complete username shown in the dashboard instead of assembling segments from memory.
| Java control | Databay value | What to verify |
|---|---|---|
ProxySelector.of(new InetSocketAddress(host, port)) | gw.databay.co, 8888 | isUnresolved() is false; every HTTP and HTTPS request uses it |
getPasswordAuthentication() | Dashboard username and password | Returned only for PROXY challenges from the gateway host and port |
jdk.http.auth.tunneling.disabledSchemes | Empty, set at launch | HTTPS URLs stop returning 407 |
connectTimeout(Duration) | For example 10 seconds | HttpConnectTimeoutException on a slow connect |
HttpRequest.Builder.timeout(Duration) | For example 30 seconds | HttpTimeoutException instead of an endless wait |
| Username segments | USER-zone-residential-countryCode-us, optionally -sessionId-abc123 | Observed country and continuity match |
https.proxyHost, https.proxyPort | gw.databay.co, 8888 | Port set explicitly; the default is 443 |
Premium Residential takes one selector at a time: country, state, city, ZIP, coordinate or ASN. Residential Flex, Datacenter and Mobile target country or continent. All pools are shared. You can request sticky windows up to 1440 minutes on Premium Residential and Residential Flex where available, or up to 120 minutes on Datacenter and Mobile. The IP can change before the window ends. Traffic is billed per GB, so keep checks small.
System Properties, ProxySelector.getDefault() and Exclusions
Without a proxy(...) call, the client uses the default proxy selector, which the HttpClient.Builder documentation says supports the JDK's proxy system properties; ProxySelector.getDefault() returns that selector if you pass it explicitly. This suits launch scripts that already own routing:
java -Dhttp.proxyHost=gw.databay.co -Dhttp.proxyPort=8888 \
-Dhttps.proxyHost=gw.databay.co -Dhttps.proxyPort=8888 \
-Dhttp.nonProxyHosts="localhost|127.*|[::1]|*.internal.example" \
-Djdk.http.auth.tunneling.disabledSchemes="" \
-jar app.jarThe Networking Properties page sets the rules to check:
http.proxyPortdefaults to 80 andhttps.proxyPortto 443, so set both to 8888 explicitly.- HTTPS reads
http.nonProxyHosts, a|-separated list with*wildcards. Keep the defaultlocalhost|127.*|[::1]entries when replacing it, and quote the value so the shell does not see a pipe. - No credential property exists for HTTP or HTTPS proxies, so names like
http.proxyUserdo nothing for the JDK clients; anAuthenticatoris still required.
ProxySelector.of(...) routes every HTTP and HTTPS request through one address, so http.nonProxyHosts does not apply to it. A client also captures the system-wide selector when built, and a later ProxySelector.setDefault(...) leaves existing clients unchanged, so configure routing at launch. HttpClient.Builder.NO_PROXY forces a direct connection.
HttpURLConnection With Proxy.Type.HTTP or SOCKS
Legacy code still uses HttpURLConnection. Pass a java.net.Proxy to openConnection and scope credentials to that connection with setAuthenticator (Java 9 and later):
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("gw.databay.co", 8888));
// SOCKS5: Proxy.Type.SOCKS with the same address
HttpURLConnection conn = (HttpURLConnection)
URI.create("https://api.ipify.org?format=json").toURL().openConnection(proxy);
conn.setAuthenticator(proxyOnly); // gateway-only Authenticator from above
conn.setConnectTimeout(10_000); // milliseconds
conn.setReadTimeout(30_000);
try (InputStream in = conn.getInputStream()) {
System.out.println(conn.getResponseCode() + " usingProxy=" + conn.usingProxy() + " "
+ new String(in.readAllBytes(), StandardCharsets.UTF_8));
}The tunnelling property applies here as well: while Basic stays disabled, HTTPS fails with an IOException starting Unable to tunnel through proxy. Proxy returns plus the proxy's status line. usingProxy() confirms a proxy was used, not which exit.
With Proxy.Type.SOCKS, SOCKS5 credentials come from the system-wide Authenticator.setDefault(...). In the OpenJDK source that request reports getRequestingProtocol() as SOCKS5 without the PROXY requestor type, so match the gateway host, port and protocol instead. The java.net.http.HttpClient documentation does not mention SOCKS, and OpenJDK connects directly when the selector's first entry is not an HTTP proxy, so keep SOCKS5 on HttpURLConnection and confirm it with the exit check. See the HTTP versus SOCKS5 comparison.
OkHttp as an Alternative Client
In OkHttp's client builder, proxy(...) sets the HTTP proxy for every connection and overrides proxySelector(...); proxyAuthenticator(...) answers proxy challenges, separate from the origin authenticator(...).
String user = System.getenv("DATABAY_PROXY_USER");
String pass = System.getenv("DATABAY_PROXY_PASS");
OkHttpClient client = new OkHttpClient.Builder()
.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("gw.databay.co", 8888)))
.proxyAuthenticator((route, response) -> {
if (response.request().header("Proxy-Authorization") != null) {
return null; // already rejected once: stop
}
return response.request().newBuilder()
.header("Proxy-Authorization", Credentials.basic(user, pass))
.build();
})
.connectTimeout(Duration.ofSeconds(10))
.callTimeout(Duration.ofSeconds(30))
.build();
Request check = new Request.Builder().url("https://api.ipify.org?format=json").build();
try (Response response = client.newCall(check).execute()) {
System.out.println(response.code() + " " + response.body().string());
}Per the OkHttp Authenticator documentation, OkHttp calls the proxy authenticator before every CONNECT with a synthetic 407 carrying an OkHttp-Preemptive challenge, so the tunnel can authenticate on its first attempt; returning null once Proxy-Authorization was already sent prevents a loop. callTimeout spans the whole call and has no limit unless set; connectTimeout covers the TCP connect and defaults to 10 seconds. The JDK tunnelling property does not govern OkHttp, but Basic credentials still cross the client-to-gateway hop unencrypted.
Verify the Observed Exit Before the Real Destination
Make the first request of every new configuration a small exit check. https://api.ipify.org?format=json returns the address it observed; Databay's What Is My IP page and https://databay.com/what-is-my-ip/json give a second reading. Record the JVM version, effective proxy settings, redacted username variant, observed IP, status and UTC time.
- Start with the complete dashboard username and confirm a 200.
- For location, copy the dashboard username for that target, such as
USER-zone-residential-countryCode-us, and compare the observed country. - For continuity, use the session form, such as
USER-zone-residential-countryCode-us-sessionId-abc123, and recheck during the session window. - Contact the real, authorized destination only after these readings match.
HttpClient reuses pooled connections, so a request on a reused tunnel may keep the same exit. A new proxy connection may select an available exit, but a different IP is not promised, and creating a client per request usually just prevents connection reuse. An observed IP describes one request; it does not prove anonymity or permission.
Troubleshoot the Proxy Hop Before the Destination Hop
Classify the first failing layer before changing anything. The proxy hop is gateway DNS, TCP to gw.databay.co:8888, proxy authentication and CONNECT; the destination hop is TLS inside the tunnel and the HTTP status. Quoted messages come from OpenJDK and can vary by version.
| Evidence | Hop and layer | Next safe check |
|---|---|---|
UnknownHostException or an unresolved address for gw.databay.co | Proxy hop: gateway DNS | Check spelling and the resolver on the same host |
UnknownHostException naming the destination | Proxy not applied | Check proxy(...), http.nonProxyHosts and any SOCKS entry given to HttpClient |
HttpConnectTimeoutException, ConnectException | Proxy hop: TCP | Check port 8888, egress rules and https.proxyPort |
| Status 407 on HTTPS only | Proxy hop: CONNECT authentication | Set the tunnelling property at launch |
No credentials provided | Authenticator returned null | Check host, port and requestor type; a destination Basic 401 also triggers it |
too many authentication attempts | Proxy hop: credentials rejected | Re-copy the dashboard username and password |
Tunnel failed, got: or Unable to tunnel through proxy | Proxy hop: CONNECT refused | 407 means authentication; other codes concern the target host and port |
SSLHandshakeException | Destination hop: TLS | Check trust store, clock and host name |
| Destination 401, 403 or 429 | Destination hop: policy | Stop; another exit is not permission |
Never answer a TLS error with a trust-all TrustManager or an accept-everything hostname verifier; both hide interception. Treat jdk.httpclient.HttpClient.log output with headers or content as sensitive and redact it before sharing. The cURL proxy guide shows the same ladder from a terminal.
Questions specific to Java HttpClient
How do I set a proxy for Java HttpClient?
Why does Java HttpClient get 407 for HTTPS URLs but not HTTP URLs?
Does the JDK support http.proxyUser and http.proxyPassword?
Can java.net.http.HttpClient use a SOCKS5 proxy?
Does http.nonProxyHosts apply to HTTPS requests?
Will each Java request use a different exit IP?
Same gateway, different control surface
Python Requests
HTTP client library
Session -> HTTPAdapter policy -> proxy hop -> approved destination
.NET HttpClient
HTTP client library
HttpClient -> SocketsHttpHandler.Proxy -> CONNECT or SOCKS5 -> observed exit
PHP Guzzle and cURL
HTTP client library
Guzzle option or CURLOPT -> libcurl handle -> CONNECT or SOCKS5 -> observed exit
Pick the network once the route works. Residential for home-connection exits with state, city, ZIP and ASN targeting; datacenter for fast server IPs on public websites. Switching is one word in the username.
Where teams run Java HttpClient behind the gateway: Proxies for web scraping · Proxies for market research
Ship Java HttpClient to production
Create an account, drop in your gateway credentials, and route your first Java HttpClient request in minutes.
Pricing, order minimums, and traffic validity vary by product.