For a Java HTTP proxy, configure the client that sends the request: use a ProxySelector and client-scoped Authenticator with JDK HttpClient, or an explicit Proxy and proxyAuthenticator with OkHttp. Keep credentials separate from URLs, verify destination certificates, and compare one direct request with the same approved destination through the proxy.
Reviewed and locally tested: October 13, 2026. We executed Windows Temurin 21.0.11 and OkHttp 4.12.0 with Okio JVM 3.6.0 and Kotlin 1.9.10. These are tested versions, not a statement that they are the newest. The examples select HTTP/1.1; other JDK builds, Android, HTTP/2 and SOCKS are outside this lab.
Choose the Java client and check its build
JDK java.net.http.HttpClient is available from Java 11. This downloadable diagnostic specifically requires Java 21 because it closes HttpClient through its newer lifecycle API. It is different from the older HttpURLConnection handler. A setting that works in one handler does not establish behavior in another. See the Java 21 HttpClient reference.
OkHttp is a separate library. This guide pins the 4.12.0 Java API rather than assuming a newer dependency has the same build requirements. The combined source needs its seven recorded Maven artifacts even when you select the JDK branch. The supplied build script downloads them locally and checks SHA-256 hashes. Run java -version and javac -version in the application environment before copying examples.
Run the tested diagnostic
Download the Java proxy source examples and read the README. The ZIP contains ProxyRequest.java, build.ps1 and the dependency manifest; it contains no credentials or binary libraries. In its extracted directory, use PowerShell:
./build.ps1
java -cp 'classes;lib/*' ProxyRequest jdk
java -cp 'classes;lib/*' ProxyRequest okhttp
Supply an approved CHECK_URL through the process environment. For proxy routing, supply a credential-free PROXY_ENDPOINT, such as http://proxy.example:8080, and separate PROXY_USER and PROXY_PASSWORD secret variables. Both credentials must be present together. This helper accepts HTTP proxy transport only. Unix Java classpaths use classes:lib/* instead of a semicolon.
For the direct control, remove those three proxy variables from the diagnostic process and request the same destination. The helper explicitly disables proxy selection in that branch. Run each command separately and inspect its JSON: a failed network check can still produce a normal process exit because the failure is reported as structured metadata.
JDK HttpClient: select and authenticate the proxy
The JDK branch sets ProxySelector.of(address) on its builder; its direct branch uses HttpClient.Builder.NO_PROXY. Supplying this selector avoids the default selector’s proxy and bypass rules. Our actual requests stayed direct with hostile JVM proxy defaults, and stayed proxied with http.nonProxyHosts=*. This is a client choice, not a change to the entire application. The builder reference documents explicit selection.
The authenticator checks RequestorType.PROXY, the configured host and port, and the Basic scheme before returning credentials. It does not answer destination-server challenges or call Authenticator.setDefault(). Credentials belong to the proxy authentication callback, not an ordinary destination Authorization header. Read username/password versus IP authentication when selecting the endpoint’s actual access method.
Why HTTP works but JDK HTTPS CONNECT returns 407
An HTTPS destination through an HTTP proxy first requires a CONNECT tunnel, then a TLS handshake with the destination. The destination’s https:// scheme does not mean your proxy endpoint uses HTTPS transport. Basic credentials sent to an HTTP proxy are not encrypted by the later destination TLS session.
The JDK’s jdk.http.auth.tunneling.disabledSchemes policy disables Basic CONNECT authentication by default. Our default JVM returned 407 even with valid fixture credentials. If your administration policy explicitly allows Basic on the selected proxy path, the owned diagnostic can be launched with:
java '-Djdk.http.auth.tunneling.disabledSchemes=' -cp 'classes;lib/*' ProxyRequest jdk
This startup option affects authentication policy across that JVM. Assess the transport before enabling it; do not edit the shared JDK installation or weaken destination TLS. The Java 21 networking properties explain the policy and authentication retry limits. Our approved local child JVM successfully established CONNECT with strict destination trust.
OkHttp: use proxyAuthenticator and stop repeated attempts
The OkHttp branch uses new Proxy(Proxy.Type.HTTP, address), with Proxy.NO_PROXY for direct requests. Its proxyAuthenticator checks the actual proxy route and returns a request with Proxy-Authorization. It declines further attempts when that header is already present. Destination authentication uses a different callback.
OkHttp also calls the proxy authenticator before CONNECT with a synthetic OkHttp-Preemptive challenge. The helper handles that separately from a real Basic challenge; a logging interceptor is not a replacement for the CONNECT callback. This behavior is described in the versioned OkHttp 4.12.0 authenticator source. OkHttp succeeded in our authenticated HTTPS fixture without the JDK tunneling switch.
Keep TLS strict and bound response reading
Leave CA_FILE unset to use normal runtime trust. For an approved private CA, supply its PEM bundle. The helper constructs a client-specific trust manager containing that bundle, replacing the default public roots for this client. Hostname verification stays enabled. Both clients rejected an untrusted certificate and a separately trusted certificate for the wrong hostname. Obtain CA material through a trusted administrative channel; see the Java 21 JSSE reference.
CONNECT_TIMEOUT_MS defaults to 2000 and REQUEST_TIMEOUT_MS to 5000. The JDK helper adds a bounded wait for its complete-body sendAsync() future and cancels on deadline. Treat a streaming body handler separately; receiving headers does not prove the body has completed. See the request timeout reference.
OkHttp sets connect, read and complete-call timeouts. The last one includes response reading, as documented in its 4.12.0 client source. Our 350 ms body-stall deadlines terminated both calls; reported request-plus-cleanup times were 440 ms for JDK and 407 ms for OkHttp. Client initialization added separate overhead. These are fixture observations, not proxy speed measurements or exact timing guarantees.
What the owned proxy lab observed
| Check | JDK HttpClient | OkHttp 4.12.0 |
|---|---|---|
| Direct and authenticated HTTP | 200; distinct actual routes | 200; distinct actual routes |
| Missing / wrong HTTP credentials | 407 / IOException | 407 / 407 |
| Missing / wrong CONNECT credentials | 407 / IOException | IOException / IOException |
| Approved CA and valid CONNECT auth | 200 with explicit Basic policy opt-in | 200 with proxy callback |
| Untrusted / wrong-hostname certificates | SSLHandshakeException | SSLHandshakeException / SSLPeerUnverifiedException |
| Redirect / incomplete body | 302 without following / TimeoutException | 302 without following / InterruptedIOException |
All 27 checks passed against owned loopback fixtures. Rejected authentication produced no destination request. Successful requests correlated destination sockets with proxy upstream sockets, and the destination received no Proxy-Authorization header. The fixtures used random disposable credentials. No commercial endpoint, public exit location, anonymity, speed or uptime was tested.
Diagnose the layer before retrying
A 407 or rejected CONNECT belongs to the proxy layer; destination 401/403 belongs elsewhere. In this helper, status zero means no response status was exposed. Different challenge sequences and client versions can produce different exception shapes, so inspect proxy-side evidence instead of searching only for a literal 407 in application output.
The diagnostic prints selected status, exception class and timing fields, without endpoints, credentials or response bodies. Keep raw errors, HTTP traces and environment dumps out of shared logs. It buffers a small response for diagnosis; add a body-size limit before adapting it to untrusted responses. For a service, reuse clients and choose its redirect/retry policy deliberately.
Continue with Developer Guides, command-line cURL, TLS and route testing methodology, and the troubleshooting checklist.
Versioned Java source files
Use the Java folder in the public source repository for the example, setup instructions, exact dependency versions and a sanitized test summary. The repository is a dated snapshot of owned examples; its loopback checks do not certify a commercial endpoint or every application configuration.
