skip to content

What out-of-the-box behaviors does the java.net.http client provide for HTTP/2, redirects, timeouts, and authentication?

level: seniorimportance: should knowfreq 48%

answer

  1. HTTP/2 default, auto-fallback to HTTP/1.1
  2. Redirects default NEVER; opt in with NORMAL/ALWAYS
  3. Two timeouts: client connectTimeout + per-request timeout
  4. No default request timeout — set one in prod
  5. Authenticator = reactive Basic; Bearer = set Authorization header yourself

basics

~20 s

The client speaks HTTP/2 by default (falling back to HTTP/1.1), can follow redirects based on a configured policy, supports a connect timeout on the client and a per-request timeout, and can authenticate via a configured Authenticator or by adding an Authorization header yourself.

solid answer

~50 s

By default HttpClient negotiates HTTP/2 and transparently falls back to HTTP/1.1; you can pin a version with .version(HTTP_1_1). Redirects are NOT followed by default (policy NEVER); set .followRedirects(Redirect.NORMAL) to follow except to/from HTTPS-downgrades, or ALWAYS. Timeouts come in two flavors: .connectTimeout(Duration) on the client (time to establish a connection) and .timeout(Duration) per HttpRequest (overall response timeout) — a per-request timeout failure surfaces as HttpTimeoutException. Authentication can be handled by a java.net.Authenticator set on the client (it answers proxy/server auth challenges, mainly Basic), or, more commonly for APIs, by setting the Authorization header on the request yourself (Bearer tokens). The client also supports WebSocket via HttpClient.newWebSocketBuilder() and HTTP/2 server push via a PushPromiseHandler. Cookies are off unless you attach a CookieHandler. Knowing these defaults avoids surprises — especially that redirects are off and that the Authenticator does not send Authorization preemptively.

go deeper

for a junior

Aware that the client supports HTTP/2 and that timeouts and redirects can be configured.

for a middle

Knows redirects default to NEVER and that there are separate connect and request timeouts; can configure both.

for a senior

Articulates HTTP/2 default + fallback, the redirect policy enum, the no-default-request-timeout pitfall, and that Authenticator is reactive Basic vs setting Bearer headers manually.

for a principal

Sets organization-wide defaults (timeouts, redirect policy, TLS/proxy, executor sizing), reasons about HTTP/2 multiplexing on the pool, and standardizes auth header handling and observability.

## Why defaults matter A platform HTTP client makes choices for you. Knowing `java.net.http`'s defaults prevents subtle bugs — like silently not following a redirect, or assuming HTTP/1.1 when HTTP/2 is in play. ## HTTP/2 (and fallback) **HTTP/2** is a newer version of the protocol that multiplexes many requests over one TCP connection (vs HTTP/1.1's one-request-at-a-time per connection), with header compression and server push. The Java client **defaults to HTTP/2** and **automatically falls back to HTTP/1.1** if the server doesn't support it. You can force a version: ```java HttpClient.newBuilder().version(HttpClient.Version.HTTP_1_1).build(); ``` or per request via `.version(...)` on the request builder. Multiplexing means several concurrent requests to the same host can share one connection — efficient, but it also means the connection pool behaves differently from HTTP/1.1. ## Redirects A **redirect** is a 3xx response telling the client to fetch a different URL (e.g. 301 Moved Permanently, 302 Found). The client's policy is `HttpClient.Redirect`: - **`NEVER`** — the **default**: 3xx responses are returned to you as-is; nothing is followed automatically. - **`NORMAL`** — follow redirects, *except* it will not redirect from HTTPS to HTTP (a security downgrade). - **`ALWAYS`** — follow all, including downgrades. ```java HttpClient.newBuilder().followRedirects(HttpClient.Redirect.NORMAL).build(); ``` The common gotcha: people expect redirects to be followed automatically (curl/browsers do); here you must opt in. After following, `response.uri()` gives the final URL. ## Timeouts There are **two distinct** timeouts: 1. **Connect timeout** — set on the *client*: `.connectTimeout(Duration.ofSeconds(5))`. Caps how long establishing the connection may take. If exceeded, an `HttpConnectTimeoutException` (a subtype of `HttpTimeoutException`) results. 2. **Request timeout** — set on the *request*: `.timeout(Duration.ofSeconds(10))`. Caps the overall time to receive the response. If exceeded, an `HttpTimeoutException` results (or the async future completes exceptionally with it). There is **no default** request timeout — without one, a slow server can block you indefinitely (or until the OS socket times out), so always set request timeouts in production. ## Authentication Two approaches: 1. **`java.net.Authenticator`** set on the client: `.authenticator(myAuthenticator)`. It responds to HTTP/proxy **auth challenges** (a 401/407 asking for credentials), primarily **Basic** auth. Crucially, it is **reactive, not preemptive** — it answers a challenge after a 401; it does not send credentials on the first request. 2. **Set the header yourself** — for token APIs this is the norm: ```java HttpRequest.newBuilder(uri) .header("Authorization", "Bearer " + token) .GET().build(); ``` For Basic preemptively, you Base64-encode `user:pass` into the `Authorization` header rather than relying on the `Authenticator`. ## Other built-ins worth knowing - **WebSocket**: `client.newWebSocketBuilder().buildAsync(uri, listener)` — full-duplex messaging built into the same client. - **HTTP/2 server push**: `sendAsync(req, handler, pushPromiseHandler)` lets you accept resources the server proactively pushes. - **Cookies**: not managed unless you set a `CookieHandler` via `.cookieHandler(new CookieManager())`. - **Proxy**: `.proxy(ProxySelector...)`; **SSL**: `.sslContext(...)` / `.sslParameters(...)`. - **Executor / connection pool**: shared and reused across requests on the same client. ## The headline gotchas - Redirects are **off by default**. - There is **no default request timeout** — set one. - The `Authenticator` is **reactive Basic**, not a place for Bearer tokens — add the header yourself. - HTTP/2 is the default, which changes connection-reuse behavior vs HTTP/1.1.

  • What's the difference between connectTimeout and a request timeout?
    connectTimeout (on the client) bounds establishing the connection; the request .timeout() bounds the overall time to get the response. The former yields HttpConnectTimeoutException, the latter HttpTimeoutException.
  • Why won't the Authenticator send your Bearer token?
    Authenticator answers reactive auth challenges (mostly Basic) after a 401; it isn't a token store and doesn't add Authorization preemptively. For Bearer/API tokens, set the Authorization header on each request yourself.
  • What does Redirect.NORMAL do that ALWAYS doesn't?
    NORMAL follows redirects but refuses an HTTPS→HTTP downgrade for security; ALWAYS follows even insecure downgrades.

saying these in an interview costs you the question

  • Assuming redirects are followed automatically (default is NEVER)
  • Assuming there's a default request timeout (there isn't)
  • Using the Authenticator for Bearer tokens or expecting preemptive auth
  • Assuming HTTP/1.1 by default and being surprised by HTTP/2 connection reuse
  • Thinking cookies are tracked without configuring a CookieHandler

context