> For the complete documentation index, see [llms.txt](https://docs.disasm.dev/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.disasm.dev/getting-started/tls-fingerprinting.md).

# TLS & Fingerprinting

A correct payload is only half of a solve. Before DataDome or Incapsula looks at your cookie, it fingerprints the connection itself - the TLS handshake (JA3/JA4), the HTTP/2 settings and frame order, and the order of your HTTP headers. If those don't look like the browser you are claiming to be, you are blocked regardless of how good the payload is.

Our solvers emulate **Chrome on Windows**. Your client has to do the same.

## What gets fingerprinted

* **TLS (JA3/JA4)** - the cipher suites, extensions and curves your client offers during the handshake. A default Go, Python `requests` or Node client has an obvious, non-browser signature.
* **HTTP/2** - the SETTINGS frame values, header-table size, window updates and pseudo-header order. Real Chrome has a specific, stable HTTP/2 fingerprint.
* **Header order** - browsers send headers in a consistent order. Reordering them, or letting a library sort them alphabetically, is a common tell.

A standard HTTP library cannot control any of this, which is why a purpose-built client is required.

## Recommended client: httpcloak

We recommend [**httpcloak**](https://github.com/sardanioss/httpcloak) ([docs](https://httpcloak.dev)). It reproduces browser-identical JA3/JA4, HTTP/2 and HTTP/3 fingerprints, preserves header order, and ships presets for current Chrome, Firefox and Safari versions. The core is written in Go, with bindings for Python, Node.js and C#.

Because our API emulates Chrome on Windows, use a matching Windows preset such as `chrome-146-windows` (or `chrome-latest`), and keep it current as Chrome releases.

{% tabs %}
{% tab title="Go" %}

```bash
go get github.com/sardanioss/httpcloak
```

```go
import (
    "context"
    "fmt"

    "github.com/sardanioss/httpcloak/client"
)

c := client.NewClient("chrome-146-windows")
defer c.Close()

resp, _ := c.Get(context.Background(), "https://example.com", nil)
fmt.Println(resp.StatusCode, resp.Protocol)
```

{% endtab %}

{% tab title="Python" %}

```bash
pip install httpcloak
```

```python
import httpcloak

r = httpcloak.get("https://example.com", preset="chrome-146-windows")
print(r.status_code, r.protocol)
```

{% endtab %}

{% tab title="Node.js" %}

```bash
npm install httpcloak
```

```javascript
import httpcloak from "httpcloak";

const session = new httpcloak.Session({ preset: "chrome-146-windows" });
const r = await session.get("https://example.com");
console.log(r.statusCode, r.protocol);
session.close();
```

{% endtab %}
{% endtabs %}

## Match the headers we return

Several endpoints hand back the client-hint values to send on your requests - DataDome returns a `headers` map (`sec-ch-ua`, `sec-ch-ua-full-version-list`, and so on), Incapsula returns `userAgent` and `sec_ch_ua`. Send those exact values and keep them stable for the lifetime of the session, alongside a matching User-Agent. The TLS fingerprint, the User-Agent and the client hints must all describe the same browser.

## Header order and recording

* Record a real Chrome session against the target with **Charles Proxy** and replicate the header order exactly.
* Avoid tools that don't preserve HTTP/2 header order, such as Proxyman or Fiddler. Per [RFC 7540](https://httpwg.org/specs/rfc7540.html) the order carries meaning, and a reordered set is detectable.
* Header order matters for the whole session, not just the solve - a clean solve followed by mis-ordered headers will still be flagged.

{% hint style="warning" %}
A few steps must be sent over **HTTP/1.1** rather than HTTP/2 (for example the DataDome interstitial POST). The individual guides call this out where it applies.
{% endhint %}
