Exploring HTTP/3 Support in Java 26’s HTTP Client API
Java 26 adds HTTP/3 support to the standard java.net.http.HttpClient API. This allows Java applications to communicate with HTTP/3-enabled servers without requiring a third-party HTTP client library. Existing applications using HttpClient can adopt HTTP/3 with relatively small code changes. The new support includes the HttpClient.Version.HTTP_3 protocol version and HTTP/3-specific configuration options, such as HttpOption.H3_DISCOVERY, for controlling how an HTTP/3 connection is discovered and established. HTTP/3 is not selected by default, so applications must explicitly request it.
1. Overview
Java’s HTTP Client API was introduced in Java 11 and already supported HTTP/1.1 and HTTP/2. Java 26 extends the same API to support HTTP/3. The main classes remain unchanged:
HttpClient– creates and manages HTTP connections.HttpRequest– represents an HTTP request.HttpResponse– represents the response returned by the server.
Java 26 adds HTTP_3 to HttpClient.Version. Therefore, developers can request HTTP/3 using the same API style previously used for HTTP/1.1 and HTTP/2.
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_3).build();
The default preferred protocol remains HTTP/2. HTTP/3 must be explicitly enabled either on the HttpClient or on an individual HttpRequest.
1.1 Understanding HTTP/3
HTTP/3 is the third major version of HTTP. Unlike HTTP/1.1 and HTTP/2, which use TCP as their underlying transport, HTTP/3 uses QUIC. QUIC operates over UDP and integrates features such as encryption, connection establishment, and independent streams. From an application developer’s perspective, the most important point is that HTTP semantics remain familiar. Applications still send GET, POST, PUT, DELETE, and other requests using headers and request bodies. The transport mechanism underneath the HTTP exchange changes, but the Java programming model remains largely the same.
1.2 Understanding Protocol Selection
Setting HTTP_3 represents a preferred protocol version rather than blindly guaranteeing that every request will use HTTP/3. The final protocol can depend on the destination server, connection discovery, network configuration, proxies, TLS configuration, and the HTTP/3 discovery mode. Java 26 introduces the HttpOption.H3_DISCOVERY request option to provide additional guidance about how the JDK should discover or establish an HTTP/3 connection.
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://example.com")).version(HttpClient.Version.HTTP_3).setOption(
HttpOption.H3_DISCOVERY, HttpOption.Http3DiscoveryMode.ANY).GET().build();
If no H3_DISCOVERY option is supplied while HTTP/3 is requested, the built-in JDK client behaves as though HttpOption.Http3DiscoveryMode.ANY had been selected. Applications can inspect the actual protocol used for a completed request using HttpResponse.version():
System.out.println(response.version());
For an HTTP/3 response, this prints: HTTP_3.
1.3 Understanding Error Handling
Network communication can fail for several reasons, such as an unreachable server, a connection timeout, TLS problems, interruption of the current thread, or an unsupported protocol configuration. A typical synchronous request should therefore handle IOException and InterruptedException.
try {
HttpResponse < String > response = client.send(request, HttpResponse.BodyHandlers.ofString());
} catch(IOException e) {
System.err.println("HTTP request failed: " + e.getMessage());
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Request interrupted.");
}
Java 26 also defines UnsupportedProtocolVersionException. It may be thrown when HTTP/3 is requested but the configured client cannot support it, for example because its SSL configuration is incompatible with HTTP/3.
2. Complete Java Example
The following example creates an HTTP client that prefers HTTP/3, sends an HTTPS GET request, and displays the response status, negotiated protocol, and part of the response body.
// Http3Example.java
import java.io.IOException;
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 Http3Example {
public static void main(String[] args) {
HttpClient client = HttpClient.newBuilder().version(HttpClient.Version.HTTP_3).connectTimeout(Duration.ofSeconds(10)).build();
HttpRequest request = HttpRequest.newBuilder().uri(URI.create("https://www.example.com/")).version(HttpClient.Version.HTTP_3).timeout(Duration.ofSeconds(15)).header("Accept", "text/html").GET().build();
try {
HttpResponse < String > response = client.send(request, HttpResponse.BodyHandlers.ofString());
System.out.println("Status Code : " + response.statusCode());
System.out.println("HTTP Version: " + response.version());
System.out.println("Content Type: " + response.headers().firstValue("content-type").orElse("Not provided"));
String body = response.body();
int previewLength = Math.min(body.length(), 200);
System.out.println("\nResponse Preview:");
System.out.println(body.substring(0, previewLength));
} catch(IOException e) {
System.err.println("Unable to complete HTTP request: " + e.getMessage());
} catch(InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("HTTP request was interrupted.");
}
}
}
2.1 Code Example
The Http3Example program demonstrates how to make an HTTP/3 request using the Java 26 HTTP Client API. It first imports the required classes for HTTP communication, URI handling, exception handling, and timeout configuration. An HttpClient is created using HttpClient.newBuilder(), with HttpClient.Version.HTTP_3 set as the preferred HTTP version and a 10-second connection timeout. Next, an HttpRequest is created for https://www.example.com/, explicitly requesting HTTP/3, setting a 15-second request timeout, adding an Accept: text/html header, and using the GET method. The request is sent synchronously using client.send(), while HttpResponse.BodyHandlers.ofString() converts the response body into a String. After receiving the response, the program prints the HTTP status code, the HTTP protocol version actually used, and the Content-Type response header. It then retrieves the response body and prints only the first 200 characters, or the complete body if it contains fewer than 200 characters, to provide a short preview. The try-catch blocks handle IOException for communication-related failures and InterruptedException if the operation is interrupted; in the latter case, the thread’s interrupted status is restored using Thread.currentThread().interrupt().
2.2 Code Run and Output
Compile and run the Http3Example.java program using Java 26. When the target server and network environment support HTTP/3, the program sends the GET request and displays the HTTP status code, the protocol version used for the response, the content type, and a short preview of the response body. A successful execution may produce output similar to the following:
Status Code : 200
HTTP Version: HTTP_3
Content Type: text/html
Response Preview:
<!doctype html>
<html>
<head>
<title>Example Domain</title>
...
The 200 status code indicates that the request was successful, while HTTP_3 confirms that HTTP/3 was used for the response. The exact content type and response preview may vary depending on the server and its response.
3. Conclusion
Java 26 brings HTTP/3 directly into the standard Java HTTP Client API. Developers can now select HttpClient.Version.HTTP_3 while continuing to use the familiar HttpClient, HttpRequest, and HttpResponse programming model. Applications should remember that HTTP/3 is not enabled by default. It must be explicitly selected, requires HTTPS, and its actual use depends on factors such as server support, network configuration, discovery behavior, TLS configuration, and proxy usage. Java 26 also provides H3_DISCOVERY for applications that need more control over HTTP/3 connection discovery. For most applications, however, adopting HTTP/3 requires only a small change: configuring the existing Java HTTP client to prefer HttpClient.Version.HTTP_3.

