Skip to content
1 GB of residential proxies, free. $0 today, card required.
Tunnel authentication runbook

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.

Java HttpClient → DatabayGateway selector, proxy-only credentials, Basic on CONNECT
Java HttpClientHTTP client library
Databay gateway modesClient support is covered below
gw.databay.co:8888
By Databay Research Team11 min runbook

Operating 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.java

connectTimeout 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.setProperty may have no effect. In PowerShell, quote the whole argument: "-Djdk.http.auth.tunneling.disabledSchemes=".
  • Know the exposure. The net.properties comment explains the restriction: Basic amounts to cleartext transmission of the password, here on the hop between the JVM and gw.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 controlDatabay valueWhat to verify
ProxySelector.of(new InetSocketAddress(host, port))gw.databay.co, 8888isUnresolved() is false; every HTTP and HTTPS request uses it
getPasswordAuthentication()Dashboard username and passwordReturned only for PROXY challenges from the gateway host and port
jdk.http.auth.tunneling.disabledSchemesEmpty, set at launchHTTPS URLs stop returning 407
connectTimeout(Duration)For example 10 secondsHttpConnectTimeoutException on a slow connect
HttpRequest.Builder.timeout(Duration)For example 30 secondsHttpTimeoutException instead of an endless wait
Username segmentsUSER-zone-residential-countryCode-us, optionally -sessionId-abc123Observed country and continuity match
https.proxyHost, https.proxyPortgw.databay.co, 8888Port 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.jar

The Networking Properties page sets the rules to check:

  • http.proxyPort defaults to 80 and https.proxyPort to 443, so set both to 8888 explicitly.
  • HTTPS reads http.nonProxyHosts, a |-separated list with * wildcards. Keep the default localhost|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.proxyUser do nothing for the JDK clients; an Authenticator is 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.

  1. Start with the complete dashboard username and confirm a 200.
  2. For location, copy the dashboard username for that target, such as USER-zone-residential-countryCode-us, and compare the observed country.
  3. For continuity, use the session form, such as USER-zone-residential-countryCode-us-sessionId-abc123, and recheck during the session window.
  4. 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.

EvidenceHop and layerNext safe check
UnknownHostException or an unresolved address for gw.databay.coProxy hop: gateway DNSCheck spelling and the resolver on the same host
UnknownHostException naming the destinationProxy not appliedCheck proxy(...), http.nonProxyHosts and any SOCKS entry given to HttpClient
HttpConnectTimeoutException, ConnectExceptionProxy hop: TCPCheck port 8888, egress rules and https.proxyPort
Status 407 on HTTPS onlyProxy hop: CONNECT authenticationSet the tunnelling property at launch
No credentials providedAuthenticator returned nullCheck host, port and requestor type; a destination Basic 401 also triggers it
too many authentication attemptsProxy hop: credentials rejectedRe-copy the dashboard username and password
Tunnel failed, got: or Unable to tunnel through proxyProxy hop: CONNECT refused407 means authentication; other codes concern the target host and port
SSLHandshakeExceptionDestination hop: TLSCheck trust store, clock and host name
Destination 401, 403 or 429Destination hop: policyStop; 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.

Troubleshooting desk

Questions specific to Java HttpClient

How do I set a proxy for Java HttpClient?
Call HttpClient.newBuilder().proxy(ProxySelector.of(new InetSocketAddress("gw.databay.co", 8888))) and add an Authenticator that returns the dashboard credentials only when getRequestorType() is PROXY and the host and port match the gateway. Set connectTimeout and a request timeout, then check the exit with https://api.ipify.org?format=json before the real destination.
Why does Java HttpClient get 407 for HTTPS URLs but not HTTP URLs?
HTTPS needs a CONNECT tunnel, and the runtime's conf/net.properties lists Basic in jdk.http.auth.tunneling.disabledSchemes, so the client will not send Basic credentials for the tunnel. Launch the JVM with -Djdk.http.auth.tunneling.disabledSchemes="" only if you accept Basic credentials on the client-to-gateway hop.
Does the JDK support http.proxyUser and http.proxyPassword?
No. The JDK Networking Properties page lists host, port and exclusion properties for HTTP and HTTPS proxies but no credential properties. Supply proxy credentials with a java.net.Authenticator, or with proxyAuthenticator when you use OkHttp.
Can java.net.http.HttpClient use a SOCKS5 proxy?
Do not plan on it. The HttpClient documentation does not mention SOCKS, and the OpenJDK implementation connects directly when the selector returns a non-HTTP entry. For SOCKS5, use HttpURLConnection with Proxy.Type.SOCKS and a default Authenticator that matches the gateway host, port and SOCKS5 protocol, then verify the observed exit.
Does http.nonProxyHosts apply to HTTPS requests?
Yes. The Networking Properties page states that the HTTPS handler uses the same nonProxyHosts property as HTTP. It has no effect on a client built with ProxySelector.of, which sends every HTTP and HTTPS request through the given address.
Will each Java request use a different exit IP?
Not necessarily. HttpClient reuses pooled connections, and a reused tunnel may keep the same exit; a new proxy connection may select an available exit, but a different IP is not promised. Use a dashboard-issued sessionId username when a workflow needs continuity, for 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.
Adjacent runbooks

Same gateway, different control surface

  • cURL

    Command-line client

    curl -> proxy auth -> CONNECT or SOCKS -> approved destination

  • Python Requests

    HTTP client library

    Session -> HTTPAdapter policy -> proxy hop -> approved destination

  • Selenium

    Browser automation

    WebDriver -> auth decision -> Databay gateway

  • .NET HttpClient

    HTTP client library

    HttpClient -> SocketsHttpHandler.Proxy -> CONNECT or SOCKS5 -> observed exit

  • Go net/http

    HTTP client library

    http.Client -> Transport.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.

Residential →Datacenter →

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.