# OkHttp User Guide

OkHttp is an HTTP client for Java and Kotlin (and Android) that is **efficient by default**. It pools connections, transparently compresses downloads, caches responses, and silently recovers from common network problems. Its request/response API is built around fluent builders and immutable value types, and it supports both synchronous (blocking) calls and asynchronous calls with callbacks.

This guide shows you how to add OkHttp to your project and use it for everyday HTTP work: GET and POST requests, headers, request bodies, authentication, caching, timeouts, TLS, WebSockets, and more. Every API, option, and value shown here is taken directly from the OkHttp 5.x source.

> [!NOTE]
> OkHttp follows modern HTTP specifications: HTTP Semantics ([RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110)), HTTP Caching ([RFC 9111](https://datatracker.ietf.org/doc/html/rfc9111)), HTTP/1.1 ([RFC 9112](https://datatracker.ietf.org/doc/html/rfc9112)), HTTP/2 ([RFC 9113](https://datatracker.ietf.org/doc/html/rfc9113)), WebSockets ([RFC 6455](https://datatracker.ietf.org/doc/html/rfc6455)), and Server-Sent Events. It is principled and intentionally avoids being overly configurable — for example, it does **not** allow `GET` with a request body.

---

## 1. Quick Start

The smallest useful program downloads a URL and prints its body as a string.

=== "Java"

    ```java
    OkHttpClient client = new OkHttpClient();

    String run(String url) throws IOException {
      Request request = new Request.Builder()
          .url(url)
          .build();

      try (Response response = client.newCall(request).execute()) {
        return response.body().string();
      }
    }
    ```

=== "Kotlin"

    ```kotlin
    val client = OkHttpClient()

    fun run(url: String): String {
      val request = Request.Builder()
          .url(url)
          .build()

      client.newCall(request).execute().use { response ->
        return response.body!!.string()
      }
    }
    ```

The three steps are always the same:

1. Build an immutable `Request` describing the URL, method, and headers.
2. Hand it to `client.newCall(request)` to get a `Call`.
3. `execute()` the call (synchronous) and read the `Response`.

> [!IMPORTANT]
> Always close the response body. The `try (Response response = ...)` form (Java) and `response.use { }` form (Kotlin) close it for you. A leaked response body holds its connection open and will eventually exhaust your connection pool. Reading the body fully with `body().string()` also closes it.

> [!TIP]
> Create **one** `OkHttpClient` and reuse it for every call in your application. Each client owns its own connection pool and thread pools; sharing one instance reuses connections and threads, which lowers latency and saves memory. See [Reuse a single client](#82-reuse-a-single-okhttpclient).

---

## 2. Installation

OkHttp is published to Maven Central under the group `com.squareup.okhttp3`. The core artifact is `okhttp`.

=== "Gradle (Kotlin DSL)"

    ```kotlin
    dependencies {
      implementation("com.squareup.okhttp3:okhttp:5.x")
    }
    ```

=== "Gradle (Groovy DSL)"

    ```groovy
    dependencies {
      implementation 'com.squareup.okhttp3:okhttp:5.x'
    }
    ```

=== "Maven"

    ```xml
    <dependency>
      <groupId>com.squareup.okhttp3</groupId>
      <artifactId>okhttp</artifactId>
      <version>5.x</version>
    </dependency>
    ```

### Using the Bill of Materials (BOM)

If you use more than one OkHttp module (for example `okhttp` plus `logging-interceptor` and `mockwebserver3`), import the **BOM** and omit individual version numbers. The BOM keeps every OkHttp artifact on the same version.

=== "Gradle (Kotlin DSL)"

    ```kotlin
    dependencies {
      implementation(platform("com.squareup.okhttp3:okhttp-bom:5.x"))
      implementation("com.squareup.okhttp3:okhttp")
      implementation("com.squareup.okhttp3:logging-interceptor")
      testImplementation("com.squareup.okhttp3:mockwebserver3")
    }
    ```

=== "Maven"

    ```xml
    <dependencyManagement>
      <dependencies>
        <dependency>
          <groupId>com.squareup.okhttp3</groupId>
          <artifactId>okhttp-bom</artifactId>
          <version>5.x</version>
          <type>pom</type>
          <scope>import</scope>
        </dependency>
      </dependencies>
    </dependencyManagement>
    ```

### Available modules

OkHttp is split into focused modules so you only depend on what you use. Every module shares the `com.squareup.okhttp3` group.

| Artifact | Package | What it adds |
|----------|---------|--------------|
| `okhttp` | `okhttp3` | Core client: `OkHttpClient`, `Request`, `Response`, `Call`, WebSockets |
| `logging-interceptor` | `okhttp3.logging` | `HttpLoggingInterceptor`, `LoggingEventListener` |
| `okhttp-tls` | `okhttp3.tls` | `HeldCertificate`, `HandshakeCertificates`, `Certificates` for test/custom TLS |
| `okhttp-sse` | `okhttp3.sse` | Server-Sent Events client (`EventSource`) |
| `okhttp-dnsoverhttps` | `okhttp3.dnsoverhttps` | DNS over HTTPS (`DnsOverHttps`) |
| `okhttp-brotli` | `okhttp3.brotli` | Brotli response decompression (`BrotliInterceptor`) |
| `okhttp-zstd` | `okhttp3.zstd` | Zstandard response decompression (`Zstd`) |
| `okhttp-coroutines` | `okhttp3.coroutines` | Kotlin coroutines support (`Call.executeAsync()`) |
| `okhttp-urlconnection` | `okhttp3` | `JavaNetAuthenticator`, `JavaNetCookieJar` adapters |
| `okhttp-java-net-cookiejar` | `okhttp3.java.net.cookiejar` | `JavaNetCookieJar` bridging `java.net.CookieHandler` |
| `mockwebserver3` | `mockwebserver3` | Scriptable mock HTTP server for tests |
| `mockwebserver3-junit4` / `mockwebserver3-junit5` | — | JUnit integration for MockWebServer |
| `okcurl` | `okhttp3.curl` | A `curl`-like CLI built on OkHttp |

### Requirements

OkHttp 5.x runs on:

- **Android 5.0+** (API level 21 and higher)
- **Java 8+**

OkHttp depends on [Okio](https://square.github.io/okio/) for high-performance I/O and on the Kotlin standard library. Both are small and strongly backward-compatible.

> [!NOTE]
> On Android, OkHttp uses [AndroidX Startup](https://developer.android.com/topic/libraries/app-startup) to initialize itself. If you disable the initializer in your manifest, your app must call `OkHttp.initialize(applicationContext)` in `Application.onCreate`.

> [!CAUTION]
> The legacy `3.12.x` branch supports Android 2.3+ (API 9+) and Java 7+, but those platforms lack TLS 1.2 and should not be used for new work. Always keep OkHttp up to date — like a web browser, staying current is an important defense against TLS and security problems.

---

## 3. Everyday Usage

### 3.1 GET a URL

The simplest call. Build a `Request` with just a URL and execute it.

=== "Java"

    ```java
    OkHttpClient client = new OkHttpClient();

    Request request = new Request.Builder()
        .url("https://publicobject.com/helloworld.txt")
        .build();

    try (Response response = client.newCall(request).execute()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
      System.out.println(response.body().string());
    }
    ```

> [!WARNING]
> `response.body().string()` loads the entire body into memory. Only use it for responses small enough to fit comfortably (a few MiB at most). For large responses, stream from `response.body().source()` (an Okio `BufferedSource`) or `response.body().byteStream()` (a `java.io.InputStream`) instead.

### 3.2 POST to a server

Create a `RequestBody` and attach it with `.post(...)`. The `RequestBody.create(content, mediaType)` factory accepts strings, byte arrays, files, `okio.ByteString`, and `okio.Path`.

=== "Java"

    ```java
    public static final MediaType JSON = MediaType.get("application/json");

    OkHttpClient client = new OkHttpClient();

    String post(String url, String json) throws IOException {
      RequestBody body = RequestBody.create(json, JSON);
      Request request = new Request.Builder()
          .url(url)
          .post(body)
          .build();
      try (Response response = client.newCall(request).execute()) {
        return response.body().string();
      }
    }
    ```

=== "Kotlin"

    ```kotlin
    val JSON = "application/json".toMediaType()

    fun post(url: String, json: String): String {
      val body = json.toRequestBody(JSON)
      val request = Request.Builder()
          .url(url)
          .post(body)
          .build()
      client.newCall(request).execute().use { response ->
        return response.body!!.string()
      }
    }
    ```

The other request methods work the same way:

| Builder method | HTTP method | Notes |
|----------------|-------------|-------|
| `.get()` | GET | Default. No body. |
| `.head()` | HEAD | No body. |
| `.post(body)` | POST | Body required. |
| `.put(body)` | PUT | Body required. |
| `.patch(body)` | PATCH | Body required. |
| `.delete()` / `.delete(body)` | DELETE | Body optional. |
| `.method(name, body)` | *custom* | Any verb, e.g. `"PROPFIND"`. |

### 3.3 Post a string

Pass a `String` and a `MediaType` to `RequestBody.create`. This example posts Markdown to GitHub's rendering API.

```java
public static final MediaType MEDIA_TYPE_MARKDOWN
    = MediaType.get("text/x-markdown; charset=utf-8");

private final OkHttpClient client = new OkHttpClient();

public void run() throws Exception {
  String postBody = "Numbers\n-------\n * 1\n * 2\n";

  Request request = new Request.Builder()
      .url("https://api.github.com/markdown/raw")
      .post(RequestBody.create(postBody, MEDIA_TYPE_MARKDOWN))
      .build();

  try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
    System.out.println(response.body().string());
  }
}
```

### 3.4 Post a file

`RequestBody.create(file, mediaType)` streams a `java.io.File` directly without loading it into memory.

```java
public static final MediaType MEDIA_TYPE_MARKDOWN
    = MediaType.get("text/x-markdown; charset=utf-8");

File file = new File("README.md");

Request request = new Request.Builder()
    .url("https://api.github.com/markdown/raw")
    .post(RequestBody.create(file, MEDIA_TYPE_MARKDOWN))
    .build();

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  System.out.println(response.body().string());
}
```

### 3.5 Post form parameters

Use `FormBody.Builder` to send `application/x-www-form-urlencoded` data. Keys and values are URL-encoded for you (use `addEncoded` if they are already encoded).

```java
RequestBody formBody = new FormBody.Builder()
    .add("search", "Jurassic Park")
    .build();
Request request = new Request.Builder()
    .url("https://en.wikipedia.org/w/index.php")
    .post(formBody)
    .build();

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  System.out.println(response.body().string());
}
```

### 3.6 Post a multipart request

`MultipartBody.Builder` builds `multipart/form-data` payloads — useful for file uploads alongside text fields. Call `setType(MultipartBody.FORM)` and add parts with `addFormDataPart`.

```java
private static final MediaType MEDIA_TYPE_PNG = MediaType.get("image/png");

RequestBody requestBody = new MultipartBody.Builder()
    .setType(MultipartBody.FORM)
    .addFormDataPart("title", "Square Logo")
    .addFormDataPart("image", "logo-square.png",
        RequestBody.create(new File("docs/images/logo-square.png"), MEDIA_TYPE_PNG))
    .build();

Request request = new Request.Builder()
    .header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
    .url("https://api.imgur.com/3/image")
    .post(requestBody)
    .build();

try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);
  System.out.println(response.body().string());
}
```

`MultipartBody` defines several standard media types you can pass to `setType`: `MIXED`, `ALTERNATIVE`, `DIGEST`, `PARALLEL`, and `FORM` (the default for form uploads).

### 3.7 Post streaming

To generate a body on the fly without buffering it, subclass `RequestBody` and write to the Okio `BufferedSink` in `writeTo`.

```java
RequestBody requestBody = new RequestBody() {
  @Override public MediaType contentType() {
    return MEDIA_TYPE_MARKDOWN;
  }

  @Override public void writeTo(BufferedSink sink) throws IOException {
    sink.writeUtf8("Numbers\n");
    sink.writeUtf8("-------\n");
    for (int i = 2; i <= 997; i++) {
      sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));
    }
  }
};
```

> [!NOTE]
> When you don't know the body length in advance, override `contentLength()` to return `-1` (the default) so OkHttp uses chunked transfer encoding.

### 3.8 Access response headers

Read a single header with `response.header(name)` (returns the last value, or `null`), or read all values for a name with `response.headers(name)`. Iterate every header by index.

```java
try (Response response = client.newCall(request).execute()) {
  if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

  System.out.println("Server: " + response.header("Server"));
  System.out.println("Date: " + response.header("Date"));
  System.out.println("Vary: " + response.headers("Vary"));
}
```

Request headers are set on the `Request.Builder`. Use `header(name, value)` to set (replacing existing values) and `addHeader(name, value)` to add (allowing duplicates).

```java
Request request = new Request.Builder()
    .url("https://api.github.com/repos/square/okhttp/issues")
    .header("User-Agent", "OkHttp Headers.java")
    .addHeader("Accept", "application/json; q=0.5")
    .addHeader("Accept", "application/vnd.github.v3+json")
    .build();
```

> [!TIP]
> `Headers.getInstant(name)` and `Headers.getDate(name)` parse date-valued headers for you, and the `Headers.Builder.add(name, Instant)` / `add(name, Date)` overloads format them correctly.

### 3.9 Asynchronous calls

Call `enqueue(Callback)` instead of `execute()` to run a request off your thread. OkHttp's dispatcher runs it on a background thread and invokes your callback when the response headers are ready.

```java
Request request = new Request.Builder()
    .url("http://publicobject.com/helloworld.txt")
    .build();

client.newCall(request).enqueue(new Callback() {
  @Override public void onFailure(Call call, IOException e) {
    e.printStackTrace();
  }

  @Override public void onResponse(Call call, Response response) throws IOException {
    try (ResponseBody responseBody = response.body()) {
      if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

      Headers responseHeaders = response.headers();
      for (int i = 0, size = responseHeaders.size(); i < size; i++) {
        System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
      }
      System.out.println(responseBody.string());
    }
  }
});
```

> [!CAUTION]
> The `onResponse` callback runs on OkHttp's dispatcher thread, **not** your UI thread. On Android, post results back to the main thread before touching UI. The response body must still be closed — read it fully or close it inside the callback.

### 3.10 Canceling a call

Call `call.cancel()` from any thread to abort an in-flight request. A call writing a request body or reading a response body throws an `IOException` when canceled.

```java
final Call call = client.newCall(request);
executor.schedule(() -> {
  call.cancel();
}, 1, TimeUnit.SECONDS);

try (Response response = call.execute()) {
  // ...
} catch (IOException e) {
  System.out.println("Call failed as expected: " + e);
}
```

Use `call.isCanceled()` to check the status. To cancel many calls at once, OkHttp tags help — see [Tag and group requests](#43-tag-and-group-requests).

---

## 4. Common Tasks

### 4.1 Parse a JSON response

OkHttp doesn't parse JSON itself — pair it with a JSON library such as [Moshi](https://github.com/square/moshi). Read from `response.body().source()` so the parser streams directly from the network.

```java
private final OkHttpClient client = new OkHttpClient();
private final Moshi moshi = new Moshi.Builder().build();
private final JsonAdapter<Gist> gistJsonAdapter = moshi.adapter(Gist.class);

public void run() throws Exception {
  Request request = new Request.Builder()
      .url("https://api.github.com/gists/c2a7c39532239ff261be")
      .build();
  try (Response response = client.newCall(request).execute()) {
    if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

    Gist gist = gistJsonAdapter.fromJson(response.body().source());
    // use gist...
  }
}
```

### 4.2 Per-call configuration with newBuilder()

`OkHttpClient` is immutable, but `client.newBuilder()` returns a builder pre-populated with the original's settings. The derived client **shares the same connection pool and thread pools**, so it's cheap to create. Use it to tweak one setting for a specific call.

```java
OkHttpClient client = new OkHttpClient();

// A short read timeout for one call:
OkHttpClient eagerClient = client.newBuilder()
    .readTimeout(500, TimeUnit.MILLISECONDS)
    .build();
try (Response response = eagerClient.newCall(request).execute()) {
  System.out.println("Response succeeded: " + response);
}
```

### 4.3 Tag and group requests

Attach an arbitrary object to a request with `Request.Builder.tag(...)`. Retrieve it later (for example in an interceptor or event listener) with `request.tag()` or the typed `request.tag(Class)` / `request.tag(KClass)` overloads. Tags are commonly used to identify and cancel a logical group of calls.

```java
Request request = new Request.Builder()
    .url("https://example.com/")
    .tag("my-screen")          // any object
    .build();
```

### 4.4 Convert a request to a curl command

For debugging, `request.toCurl()` returns an equivalent `curl` command line. Pass `true` to include header values that would otherwise be redacted.

```java
String curl = request.toCurl();
System.out.println(curl);
```

### 4.5 Work with URLs

`HttpUrl` is OkHttp's typed URL model. Parse a string with `HttpUrl.get(string)` (throws on a malformed URL) or `HttpUrl.parse(string)` (returns `null` on failure). Build and modify URLs with `HttpUrl.Builder`.

```java
HttpUrl url = HttpUrl.get("https://example.com/search")
    .newBuilder()
    .addQueryParameter("q", "sharks")
    .addQueryParameter("lang", "en")
    .build();

Request request = new Request.Builder().url(url).build();
```

Useful `HttpUrl` accessors include `scheme()`, `host()`, `port()`, `encodedPath()`, `pathSegments()`, `queryParameter(name)`, `queryParameterNames()`, `fragment()`, `isHttps()`, `redact()` (a safe-to-log form with credentials stripped), and `topPrivateDomain()`.

### 4.6 Use Kotlin coroutines

With the `okhttp-coroutines` module you can `await` a call from a coroutine using `Call.executeAsync()`. The call is canceled automatically if the coroutine is canceled.

```kotlin
import okhttp3.coroutines.executeAsync

suspend fun fetch(url: String): String {
  val request = Request.Builder().url(url).build()
  client.newCall(request).executeAsync().use { response ->
    return withContext(Dispatchers.IO) { response.body!!.string() }
  }
}
```

---

## 5. Working with Connections & Performance

### 5.1 Configure timeouts

Timeouts are set on `OkHttpClient.Builder` and apply to every call made by that client. Each accepts either a `(long, TimeUnit)` pair, a `java.time.Duration`, or a Kotlin `Duration`. A value of `0` means "no timeout".

```java
OkHttpClient client = new OkHttpClient.Builder()
    .connectTimeout(5, TimeUnit.SECONDS)
    .writeTimeout(5, TimeUnit.SECONDS)
    .readTimeout(5, TimeUnit.SECONDS)
    .callTimeout(10, TimeUnit.SECONDS)
    .build();
```

| Timeout | Default | Applies to |
|---------|---------|-----------|
| `connectTimeout` | 10 s | Establishing the TCP socket to the host. |
| `readTimeout` | 10 s | The socket and each individual read (including reading the response body). |
| `writeTimeout` | 10 s | Each individual write of the request body. |
| `callTimeout` | 0 (off) | The **entire** call: DNS + connect + request + server processing + response, including all redirects and retries. |
| `pingInterval` | 0 (off) | HTTP/2 and WebSocket keep-alive pings. |
| `webSocketCloseTimeout` | 60 s | Graceful WebSocket shutdown after `close()`. |

> [!IMPORTANT]
> For WebSockets and duplex calls, `callTimeout` only applies to the initial setup, not the lifetime of the connection.

> [!TIP]
> Set a `pingInterval` (e.g. 30 seconds) on long-lived HTTP/2 connections and WebSockets. If the server fails to answer a ping with a pong within the interval, OkHttp assumes connectivity was lost and fails the connection, letting you reconnect promptly.

### 5.2 Connection reuse & the connection pool

OkHttp automatically keeps a pool of idle connections and reuses them. URLs that share the same **address** (host, port, and client TLS/protocol settings) can share a TCP socket, which lowers latency and saves battery. HTTP/2 connections are multiplexed so many concurrent requests share one socket.

You generally don't manage the pool yourself, but you can inspect or evict it:

```java
ConnectionPool pool = client.connectionPool();
System.out.println("Open connections: " + pool.connectionCount());
System.out.println("Idle connections: " + pool.idleConnectionCount());
pool.evictAll(); // close all pooled connections
```

### 5.3 Fast fallback (Happy Eyeballs)

Since OkHttp 5.0, `OkHttpClient` enables **fast fallback** by default. When a host has several IP addresses (typically IPv6 and IPv4), OkHttp races TCP connections to them concurrently and keeps whichever connects first, alternating address families starting with IPv6 and pacing new attempts 250 ms apart. This implements Happy Eyeballs ([RFC 6555](https://datatracker.ietf.org/doc/html/rfc6555)) and improves connect latency on dual-stack networks.

```java
// Revert to the OkHttp 4.x sequential behaviour if needed:
OkHttpClient client = new OkHttpClient.Builder()
    .fastFallback(false)
    .build();
```

### 5.4 Dispatcher: limit concurrency

For asynchronous calls, the `Dispatcher` enforces concurrency limits. Defaults: **64** requests overall (`maxRequests`) and **5** per host (`maxRequestsPerHost`).

```java
OkHttpClient client = new OkHttpClient();
client.dispatcher().setMaxRequests(128);
client.dispatcher().setMaxRequestsPerHost(8);
```

You can inspect `queuedCallsCount()` and `runningCallsCount()`, or call `dispatcher().cancelAll()` to cancel everything.

### 5.5 Choose HTTP protocols

By default OkHttp prefers HTTP/2 and falls back to HTTP/1.1. Override with `protocols(...)` only to work around misbehaving servers. ALPN negotiates the protocol over HTTPS.

```java
OkHttpClient client = new OkHttpClient.Builder()
    .protocols(Arrays.asList(Protocol.HTTP_1_1))
    .build();
```

Supported `Protocol` values include `HTTP_1_1`, `HTTP_2`, and `H2_PRIOR_KNOWLEDGE` (cleartext HTTP/2 without negotiation). If you include `H2_PRIOR_KNOWLEDGE` it must be the only protocol, and HTTPS URLs are then unsupported. The list must contain either `H2_PRIOR_KNOWLEDGE` or `HTTP_1_1`, and must never contain `HTTP_1_0`.

---

## 6. Caching

OkHttp ships an optional, **off-by-default** response cache that follows RFC-correct, browser-like behaviour. Enable it by attaching a `Cache` to your client.

```kotlin
val client = OkHttpClient.Builder()
    .cache(Cache(
        directory = File(application.cacheDir, "http_cache"),
        maxSize = 50L * 1024L * 1024L // 50 MiB
    ))
    .build()
```

In Java, the constructor takes a `File` and a max size in bytes:

```java
int cacheSize = 10 * 1024 * 1024; // 10 MiB
Cache cache = new Cache(cacheDirectory, cacheSize);
OkHttpClient client = new OkHttpClient.Builder().cache(cache).build();
```

Once enabled, repeat requests can be served from cache without touching the network. Inspect whether a response came from the cache or the network:

```java
try (Response response = client.newCall(request).execute()) {
  System.out.println("network response:  " + response.networkResponse());
  System.out.println("cache response:    " + response.cacheResponse());
}
```

- `cacheResponse()` is non-null when the cache was consulted.
- `networkResponse()` is non-null when the network was used.
- On a **conditional hit** both are non-null: OkHttp revalidated with the server (e.g. `If-None-Match`) and the server returned `304 Not Modified`, so the cached body is reused.

> [!IMPORTANT]
> A response is only cached if its body is fully read, canceled, or stalled. If valid cacheable responses aren't being stored, make sure you consume the body.

Manage the cache directly:

| Operation | Code |
|-----------|------|
| Clear everything | `cache.evictAll()` |
| Delete the cache | `cache.delete()` |
| Remove specific URLs | iterate `cache.urls()` and call `remove()` |
| Inspect stats | `cache.hitCount()`, `cache.networkCount()`, `cache.requestCount()` |

> [!CAUTION]
> A cache directory must be owned exclusively by a single `Cache` instance. Pointing two clients at the same directory corrupts it.

You can override per-request caching with `CacheControl`. For example, force a cache-only read or a network-only read:

```java
Request request = new Request.Builder()
    .url(url)
    .cacheControl(CacheControl.FORCE_CACHE)   // or FORCE_NETWORK
    .build();
```

`CacheControl.Builder` exposes the full set of directives: `maxAge`, `maxStale`, `minFresh`, `noCache`, `noStore`, `noTransform`, `onlyIfCached`, and `immutable`.

---

## 7. HTTPS, TLS & Security

OkHttp balances **connectivity** (reaching as many servers as possible) with **security** (verifying servers and using strong ciphers). By default it trusts the host platform's certificate authorities and attempts a modern TLS connection.

### 7.1 Connection specs

A `ConnectionSpec` decides which TLS versions and cipher suites to offer. OkHttp provides four:

| Spec | Purpose |
|------|---------|
| `RESTRICTED_TLS` | Strictest; for stricter compliance requirements. |
| `MODERN_TLS` | Secure default; connects to modern HTTPS servers. |
| `COMPATIBLE_TLS` | Connects to secure but not-current servers. |
| `CLEARTEXT` | Insecure; used for `http://` URLs. |

By default a client uses `MODERN_TLS` (with `CLEARTEXT` allowed for plaintext URLs). Allow a fallback to older servers:

```java
OkHttpClient client = new OkHttpClient.Builder()
    .connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
    .build();
```

You can also build a custom spec restricted to specific `TlsVersion` and `CipherSuite` values:

```java
ConnectionSpec spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
    .tlsVersions(TlsVersion.TLS_1_2)
    .cipherSuites(
        CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
        CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
    .build();
```

### 7.2 Certificate pinning

Pinning restricts which certificates are trusted for a host, defending against compromised certificate authorities. Compute pins from the SHA-256 of a certificate's public key.

```java
OkHttpClient client = new OkHttpClient.Builder()
    .certificatePinner(
        new CertificatePinner.Builder()
            .add("publicobject.com", "sha256/Vjs8r4z+80wjNcr1YKepWQboSIRi63WsWXhIMN+eWys=")
            .build())
    .build();
```

> [!CAUTION]
> Certificate pinning limits your server team's ability to rotate TLS certificates. **Do not pin without the blessing of your server's TLS administrator** — a rotation you didn't coordinate will take your app offline.

To discover a host's current pins, connect once and print them:

```java
for (Certificate certificate : response.handshake().peerCertificates()) {
  System.out.println(CertificatePinner.pin(certificate));
}
```

### 7.3 Custom trusted certificates

To trust your own certificate authority (e.g. a private corporate CA), supply an `SSLSocketFactory` and `X509TrustManager`. The `okhttp-tls` module's `HandshakeCertificates` makes this straightforward:

```java
HandshakeCertificates certificates = new HandshakeCertificates.Builder()
    .addTrustedCertificate(myCaCertificate)   // an X509Certificate
    // .addPlatformTrustedCertificates()      // also trust the platform CAs
    .build();

OkHttpClient client = new OkHttpClient.Builder()
    .sslSocketFactory(certificates.sslSocketFactory(), certificates.trustManager())
    .build();
```

`Certificates.decodeCertificatePem(pem)` parses a PEM string into an `X509Certificate`.

> [!WARNING]
> Prefer the two-argument `sslSocketFactory(SSLSocketFactory, X509TrustManager)`. The single-argument overload is deprecated because OkHttp must use reflection to recover the trust manager, which is fragile.

### 7.4 Generate certificates for tests

For tests, `okhttp-tls`'s `HeldCertificate.Builder` mints self-signed certificates and CAs in code — no `keytool` required.

```java
HeldCertificate localhostCertificate = new HeldCertificate.Builder()
    .addSubjectAlternativeName("localhost")
    .build();
```

The builder supports `commonName`, `organizationalUnit`, `addSubjectAlternativeName`, `validityInterval` / `duration`, `serialNumber`, `certificateAuthority(maxIntermediates)`, `signedBy(otherCert)`, and key choices `rsa2048()` / `ecdsa256()`.

### 7.5 Authentication

Set an `Authenticator` to respond to `401`/`407` challenges automatically. It is called when the server demands credentials; return a new `Request` with an `Authorization` header, or `null` to give up. `Credentials.basic(user, pass)` builds an HTTP Basic credential.

```java
OkHttpClient client = new OkHttpClient.Builder()
    .authenticator((route, response) -> {
      if (response.request().header("Authorization") != null) {
        return null; // Already tried; give up to avoid an infinite loop.
      }
      String credential = Credentials.basic("user", "password");
      return response.request().newBuilder()
          .header("Authorization", credential)
          .build();
    })
    .build();
```

> [!IMPORTANT]
> Always check whether you already attached an `Authorization` header and return `null` if so. Without this guard a server that keeps returning `401` causes an infinite authentication loop.

Use `proxyAuthenticator(...)` for proxy (`407`) challenges. The `okhttp-urlconnection` module provides `JavaNetAuthenticator`, which bridges to `java.net.Authenticator`.

---

## 8. Logging, Cookies & Other Add-ons

### 8.1 HTTP logging

The `logging-interceptor` module logs request and response lines, headers, and bodies. Add it as an **application interceptor** (the last one added, so it logs the final outgoing request).

```java
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(logging)
    .build();
```

Levels:

| Level | Logs |
|-------|------|
| `NONE` | Nothing. |
| `BASIC` | Request and response lines. |
| `HEADERS` | Lines + headers. |
| `BODY` | Lines + headers + bodies. |

> [!CAUTION]
> `BODY` and `HEADERS` can leak `Authorization`, `Cookie`, and other sensitive data into your logs. Redact specific headers with `logging.redactHeader("Authorization")` and query parameters with `logging.redactQueryParams("token")`. Never ship `BODY` logging to production for authenticated requests.

For timing/metric-style logs of each network phase, use `LoggingEventListener.Factory` instead:

```java
OkHttpClient client = new OkHttpClient.Builder()
    .eventListenerFactory(new LoggingEventListener.Factory())
    .build();
```

### 8.2 Reuse a single OkHttpClient

```java
public final OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(new HttpLoggingInterceptor())
    .cache(new Cache(cacheDir, cacheSize))
    .build();
```

> [!TIP]
> OkHttp never requires explicit shutdown — idle threads and connections release themselves. If you must release resources aggressively (e.g. a short-lived tool), call `client.dispatcher().executorService().shutdown()`, `client.connectionPool().evictAll()`, and `client.cache().close()` (if a cache is set).

### 8.3 Cookies

By default OkHttp ignores cookies (`CookieJar.NO_COOKIES`). Provide a `CookieJar` to store and send them. To reuse `java.net.CookieManager`, use `JavaNetCookieJar` from the `okhttp-java-net-cookiejar` module:

```java
CookieManager cookieManager = new CookieManager();
cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);

OkHttpClient client = new OkHttpClient.Builder()
    .cookieJar(new JavaNetCookieJar(cookieManager))
    .build();
```

### 8.4 Response decompression (gzip / brotli / zstd)

OkHttp transparently requests and decompresses **gzip** with no configuration. For **Brotli** or **Zstandard**, add the matching interceptor from `okhttp-brotli` or `okhttp-zstd`:

```java
OkHttpClient client = new OkHttpClient.Builder()
    .addInterceptor(BrotliInterceptor.INSTANCE)
    .build();
```

The generic `CompressionInterceptor` accepts any combination of decompression algorithms (`Gzip`, `Brotli`, `Zstd`).

### 8.5 DNS over HTTPS

The `okhttp-dnsoverhttps` module resolves names via an encrypted DoH endpoint. Build a `DnsOverHttps` and set it as the client's `Dns`.

```java
DnsOverHttps dns = new DnsOverHttps.Builder()
    .client(bootstrapClient)
    .url(HttpUrl.get("https://dns.google/dns-query"))
    .build();

OkHttpClient client = bootstrapClient.newBuilder()
    .dns(dns)
    .build();
```

Builder options include `includeIPv6(bool)`, `post(bool)`, `resolvePrivateAddresses(bool)`, `resolvePublicAddresses(bool)`, `bootstrapDnsHosts(...)`, and `systemDns(dns)`.

---

## 9. WebSockets

`OkHttpClient` is also a `WebSocket.Factory`. Open a socket with `newWebSocket(request, listener)` and react to events by overriding `WebSocketListener`.

```java
Request request = new Request.Builder()
    .url("wss://echo.websocket.org")
    .build();

WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
  @Override public void onOpen(WebSocket webSocket, Response response) {
    webSocket.send("Hello");
  }
  @Override public void onMessage(WebSocket webSocket, String text) {
    System.out.println("Received: " + text);
  }
  @Override public void onClosing(WebSocket webSocket, int code, String reason) {
    webSocket.close(1000, null);
  }
  @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) {
    t.printStackTrace();
  }
});
```

Send text with `send(String)` or binary with `send(ByteString)`; both return `false` if the message couldn't be enqueued. Close gracefully with `close(code, reason)` (use code `1000` for normal closure) or abort immediately with `cancel()`. `queueSize()` reports bytes still waiting to be written.

> [!TIP]
> WebSocket messages above `minWebSocketMessageToCompress` bytes (default **1024**) are compressed when the server supports per-message deflate. Set it to `0` on the client builder to compress all outbound messages.

---

## 10. Server-Sent Events (SSE)

The `okhttp-sse` module consumes `text/event-stream` responses. Create an `EventSource.Factory` from your client and supply an `EventSourceListener`.

```java
EventSource.Factory factory = EventSources.createFactory(client);

Request request = new Request.Builder()
    .url("https://example.com/events")
    .build();

factory.newEventSource(request, new EventSourceListener() {
  @Override public void onEvent(EventSource source, String id, String type, String data) {
    System.out.println("event: " + type + " data: " + data);
  }
  @Override public void onFailure(EventSource source, Throwable t, Response response) {
    if (t != null) t.printStackTrace();
  }
});
```

Cancel the stream with `eventSource.cancel()`.

---

## 11. Interceptors

Interceptors observe, rewrite, retry, and short-circuit calls. An interceptor implements one method, `intercept(Chain)`, and **must** call `chain.proceed(request)` to produce a response (unless it deliberately short-circuits).

```java
class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();
    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        request.url(), (t2 - t1) / 1e6d, response.headers()));
    return response;
  }
}
```

### Application vs. network interceptors

OkHttp has two interceptor chains. Register with `addInterceptor` (application) or `addNetworkInterceptor` (network).

| | Application interceptors | Network interceptors |
|---|---|---|
| Registered with | `addInterceptor()` | `addNetworkInterceptor()` |
| See redirects & retries | No — invoked once | Yes — invoked per network request |
| Invoked for cache hits | Yes | No |
| Sees OkHttp-injected headers (`Accept-Encoding`, `If-None-Match`) | No | Yes |
| May short-circuit (skip `proceed`) | Yes | No |
| May call `proceed` multiple times | Yes | No (exactly once) |
| Access to the `Connection` | No | Yes |

A common application interceptor rewrites the outgoing request — for instance adding a header to every call:

```java
Interceptor headerInterceptor = chain -> {
  Request request = chain.request().newBuilder()
      .header("Authorization", "Bearer " + token)
      .build();
  return chain.proceed(request);
};
```

> [!NOTE]
> Application interceptors can adjust per-call timeouts via the chain's `withConnectTimeout`, `withReadTimeout`, and `withWriteTimeout` methods.

---

## 12. Events & Metrics

To capture metrics on every call (size, frequency, and the timing of each network phase), subclass `EventListener` and override the events you care about. In a successful call with no redirects, events fire in this order:

```text
callStart → dnsStart → dnsEnd → connectStart → secureConnectStart →
secureConnectEnd → connectEnd → connectionAcquired → requestHeadersStart →
requestHeadersEnd → responseHeadersStart → responseHeadersEnd →
responseBodyStart → responseBodyEnd → connectionReleased → callEnd
```

Register a listener (one shared instance) or a factory (a fresh listener per call, so you can hold call-specific state safely):

```java
OkHttpClient client = new OkHttpClient.Builder()
    .eventListener(myListener)              // shared
    // .eventListenerFactory(myFactory)     // per-call
    .build();
```

> [!TIP]
> Use an `EventListener.Factory` to sample metrics — return your listener for, say, 10% of calls and `EventListener.NONE` for the rest. Cache events (`cacheHit`, `cacheMiss`, `cacheConditionalHit`) are delivered through the same listener.

---

## 13. The okcurl Command-Line Tool

The `okcurl` module is a `curl`-like CLI built on OkHttp's engine — handy for testing OkHttp's HTTP/2 and TLS behaviour against a server.

```bash
okcurl https://square.com/robots.txt
```

| Option | Description |
|--------|-------------|
| `-X`, `--request <method>` | Request command to use. |
| `-d`, `--data <data>` | HTTP POST data. |
| `-H`, `--header <header>` | Custom header (repeatable). |
| `-A`, `--user-agent <ua>` | User-Agent to send (default `okcurl/<version>`). |
| `--connect-timeout <sec>` | Maximum time for connection. |
| `--read-timeout <sec>` | Maximum time for reading data. |
| `--call-timeout <sec>` | Maximum time for the entire call. |
| `-L`, `--location` | Follow redirects. |
| `-k`, `--insecure` | Allow connections to SSL sites without valid certs. |
| `-i`, `--include` | Include protocol headers in the output. |
| `--frames` | Log HTTP/2 frames to stderr. |
| `-e`, `--referer <url>` | Referer URL. |
| `-v`, `--verbose` | Verbose output. |
| `--sslDebug` | Output SSL debug information. |

> [!CAUTION]
> `-k` / `--insecure` disables all certificate and hostname verification. Use it only for local testing — never against a server handling real data.

---

## 14. Testing with MockWebServer

The `mockwebserver3` module starts a real HTTP server you script with canned responses, so your tests exercise the actual network stack. Enqueue `MockResponse` objects; the server returns them in order.

```java
MockWebServer server = new MockWebServer();
server.enqueue(new MockResponse.Builder()
    .code(200)
    .body("hello, world!")
    .build());
server.start();

HttpUrl baseUrl = server.url("/v1/chat/");
// ... point your client at baseUrl and make requests ...

RecordedRequest request = server.takeRequest();
assertEquals("/v1/chat/", request.getPath());

server.shutdown();
```

`MockResponse.Builder` can simulate realistic conditions: `bodyDelay`, `headersDelay`, `throttleBody`, `chunkedBody`, `failHandshake()` (TLS failure), `add100Continue()`, push promises, and even attach a `WebSocketListener`. JUnit integration is available via `mockwebserver3-junit4` and `mockwebserver3-junit5`.

> [!TIP]
> To test HTTPS, combine MockWebServer with `okhttp-tls`: mint a `HeldCertificate` for `localhost`, call `server.useHttps(serverCertificates.sslSocketFactory())`, and trust that certificate in your client.

---

## 15. Troubleshooting

### Error: `java.io.IOException: Unexpected code Response{...}`

**Cause:** This is application code throwing — `response.isSuccessful()` returned `false` (the status code wasn't in 200–299). It is not an OkHttp failure.

**Solution:** Inspect `response.code()` and `response.body().string()` to see what the server returned, and handle non-2xx codes deliberately.

**Prevention:** Branch on `response.code()` rather than treating every response as success.

### Error: `java.lang.IllegalStateException: closed`

**Cause:** You read or accessed a response body after it was already closed (for example, calling `body().string()` twice, or reading the body outside the `try`/`use` block that closed it).

**Solution:** Read the body exactly once and store the result. `body().string()` consumes and closes the stream.

**Prevention:** Treat `ResponseBody` as single-use. If you need the body twice, capture it in a variable.

### Error: `javax.net.ssl.SSLProtocolException: SSL handshake aborted` / `SSLHandshakeException`

**Cause:** The client and server share no common TLS version and cipher suite. This depends on your JVM/Android version, OkHttp version, and the server's configuration.

**Solution:** Add `ConnectionSpec.COMPATIBLE_TLS` to allow older configurations, or test the server at [Qualys SSL Labs](https://www.ssllabs.com/ssltest/). On older Android devices, adopt [Google Play Services' ProviderInstaller](https://developer.android.com/training/articles/security-gms-provider) to modernize the security provider.

**Prevention:** Keep OkHttp current — it tracks the evolving TLS ecosystem and drops weak ciphers as attacks emerge.

### Error: `javax.net.ssl.SSLPeerUnverifiedException: Certificate pinning failure!`

**Cause:** The server's certificate doesn't match any configured pin — usually because the server rotated its certificate, or you pinned the wrong key.

**Solution:** Re-derive the current pins (see [Certificate pinning](#72-certificate-pinning)) and update your `CertificatePinner`.

**Prevention:** Coordinate pin rotation with your server's TLS administrator and pin the CA's key (which rotates less often) rather than the leaf certificate where possible.

### Error: `java.net.SocketTimeoutException: timeout`

**Cause:** A `connectTimeout`, `readTimeout`, `writeTimeout`, or `callTimeout` elapsed.

**Solution:** Increase the relevant timeout for slow endpoints, or set `callTimeout` to bound the total time of a call regardless of which phase is slow.

**Prevention:** Choose timeouts that match your slowest acceptable endpoint; remember the defaults are 10 s for connect/read/write and unlimited for the whole call.

### Error: `java.io.InterruptedIOException` after calling `cancel()`

**Cause:** Expected — a canceled call that was mid-flight throws to unwind cleanly.

**Solution:** Catch it and treat it as a normal cancellation, checking `call.isCanceled()`.

**Prevention:** Distinguish user-initiated cancellation from genuine failures using `call.isCanceled()`.

### Symptom: Cacheable responses aren't being cached

**Cause:** The response body was never fully consumed.

**Solution:** Read the body to completion (e.g. `body().string()` or drain `body().source()`), cancel it, or let it stall — partial reads aren't cached.

**Prevention:** Always close/consume response bodies; this also prevents connection-pool leaks.

### Symptom: `IllegalStateException: cache is closed` / corrupted cache

**Cause:** Two `Cache` instances (or two clients) point at the same directory, or you used a cache after calling `close()`.

**Solution:** Use one `Cache` per directory and one shared client.

**Prevention:** Treat the cache directory as exclusively owned by a single instance.

### Symptom: Too many requests queue up under load

**Cause:** The `Dispatcher` limits async calls to 64 overall and 5 per host by default.

**Solution:** Raise `dispatcher().setMaxRequests(...)` and `setMaxRequestsPerHost(...)` to match your workload.

**Prevention:** Size the dispatcher for your concurrency needs and provide an executor that can actually run that many threads.

### Android: `ExceptionInInitializerError` / platform initializer errors on startup

**Cause:** OkHttp's AndroidX Startup initializer was disabled or ran before the application context was ready (see community issues [#9262](https://github.com/square/okhttp/issues/9262), [#9462](https://github.com/square/okhttp/issues/9462)).

**Solution:** Call `OkHttp.initialize(applicationContext)` in your `Application.onCreate` if you disabled the manifest initializer.

**Prevention:** Either keep the AndroidX Startup initializer enabled or initialize explicitly and early.

---

## 16. FAQ

**Q: Should I create a new OkHttpClient for each request?**
No. Create one and reuse it everywhere. Each client owns connection and thread pools; reusing one instance reuses connections and threads. When you need a variation, use `client.newBuilder()` — the derived client shares the pools.

**Q: Do I have to shut the client down?**
No. Idle threads and connections are released automatically. Only shut down explicitly (dispatcher executor + connection pool + cache) if you need to free resources aggressively.

**Q: Why must I close the response body?**
Each response body holds the connection it came in on. If you don't close it, the connection can't return to the pool and you'll eventually run out. Reading the body fully (`string()`, `bytes()`, draining `source()`) closes it; otherwise use try-with-resources / `use {}`.

**Q: Can I send a GET request with a body?**
No. OkHttp deliberately disallows `GET` with a body, per the HTTP specs. Use `POST` or another method if you need to send a body.

**Q: Is gzip handled automatically?**
Yes. OkHttp adds `Accept-Encoding: gzip` and transparently decompresses gzip responses (and drops the now-inaccurate `Content-Encoding`/`Content-Length` headers). For Brotli or Zstandard, add the `okhttp-brotli` or `okhttp-zstd` interceptor.

**Q: How do I follow (or stop following) redirects?**
Redirects are followed by default. Disable with `followRedirects(false)`. Cross-scheme redirects (HTTP↔HTTPS) are controlled separately by `followSslRedirects(...)`.

**Q: How do I send Basic authentication?**
For a one-off, add the header yourself: `.header("Authorization", Credentials.basic(user, pass))`. To respond to `401` challenges automatically, set an `Authenticator` and guard against retry loops.

**Q: What's the difference between an application and a network interceptor?**
Application interceptors run once per call and see your original request; network interceptors run once per network request (so they see redirects and retries) and have access to the live `Connection`. See [Interceptors](#11-interceptors).

**Q: How do I parse JSON?**
OkHttp doesn't parse JSON. Stream `response.body().source()` into a JSON library such as Moshi, Gson, or kotlinx.serialization.

**Q: Does OkHttp support HTTP/2?**
Yes — it's preferred by default and negotiated via ALPN over HTTPS. HTTP/2 connections are multiplexed so concurrent requests to one host share a socket. `H2_PRIOR_KNOWLEDGE` enables cleartext HTTP/2.

**Q: How do I cancel a request?**
Call `call.cancel()` from any thread. In-flight reads/writes throw `IOException`; check `call.isCanceled()` to distinguish cancellation from failure. Cancel all dispatcher work with `dispatcher().cancelAll()`.

**Q: How do I use OkHttp with Kotlin coroutines?**
Add `okhttp-coroutines` and call `client.newCall(request).executeAsync()` from a suspend function. The call is canceled if the coroutine is canceled.

**Q: How do I test code that uses OkHttp?**
Use `mockwebserver3` to run a real local server with scripted responses, and point your client at `server.url(...)`. It exercises the genuine HTTP stack and can simulate delays, throttling, and TLS failures.

**Q: Which Java/Android versions are supported?**
OkHttp 5.x requires Android 5.0+ (API 21) and Java 8+. The unsupported legacy `3.12.x` branch covers older platforms but lacks TLS 1.2.

**Q: How do I trust a self-signed or private-CA certificate?**
Build `HandshakeCertificates` from the `okhttp-tls` module with `addTrustedCertificate(...)` and pass its `sslSocketFactory()`/`trustManager()` to the client. For tests, mint certificates with `HeldCertificate.Builder`.

---

_Generated June 2026 from square/okhttp at 7a35098. AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it. Unofficial; not affiliated with or reviewed by the project._
