---
title: "O que é fingerprint TLS e como funciona o JA3?"
description: "O fingerprint TLS é um identificador tirado das listas de cifras e extensões do ClientHello. Como JA3 e JA4 são calculados e o que um proxy muda de fato."
url: https://proxynet.io/pt-br/blog/tls-fingerprinting
date: 2026-09-19
author: "Acar Diveroli"
category: "Web scraping, Proxy 101"
lang: pt-BR
---

# O que é fingerprint TLS e como funciona o JA3?

Você tem um script escrito em Python. Ele sai por um proxy, o endereço IP muda a cada requisição e no cabeçalho `User-Agent` você colocou uma linha atual do Chrome. Mesmo assim o site responde `403` logo na primeira requisição. Abra o mesmo endereço pelo mesmo IP em um navegador de verdade e a página carrega. A diferença está em um pacote que viaja antes de qualquer cabeçalho HTTP: a mensagem `ClientHello`, que abre a conexão criptografada. Olhando como essa mensagem é montada, um site percebe se está falando com o Chrome, com o Python ou com o curl sem ler um único cabeçalho.

Neste texto vemos o handshake TLS, os campos dentro do `ClientHello`, como a string JA3 é calculada a partir desses campos e o que o JA4 faz de diferente. Depois passamos à pergunta que mais chega até nós: um proxy muda o fingerprint TLS? No final há um exemplo em Python já testado que mostra localmente a string JA3 do seu próprio cliente.

> **Nota: Resposta rápida**
>
> Um fingerprint TLS é um resumo produzido a partir das listas de versão, suítes de cifra, extensões e curvas da mensagem `ClientHello` que um cliente envia ao abrir uma conexão criptografada. O JA3 junta essas listas na ordem e tira o hash MD5; o JA4 ordena as listas e acrescenta um prefixo legível. O valor identifica o **software**, não a pessoa: todo Chrome da mesma versão carrega o mesmo fingerprint. Um túnel HTTP `CONNECT` e um proxy SOCKS5 não encostam nessa mensagem, ou seja, o proxy muda o IP, não o fingerprint TLS.

## O que é o handshake TLS?

TLS é o protocolo que criptografa o tráfego entre o navegador e o site; o `https://` na barra de endereços mostra que você está usando ele. Antes de a criptografia começar, os dois lados precisam combinar qual versão, qual suíte de cifra e qual chave usar. Essa negociação curta se chama handshake. A versão atual, o TLS 1.3, está definida na [RFC 8446](https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2), e o handshake corre mais ou menos assim:

1. O cliente se conecta ao servidor por TCP e envia uma mensagem `ClientHello`. Nela vão as suítes de cifra e as extensões que ele suporta, além da sua parcela de chave.
2. O servidor escolhe da lista e responde com `ServerHello`. Daí em diante os dois lados conseguem derivar a chave compartilhada.
3. O servidor envia o certificado e uma mensagem `Finished` que prova a integridade do handshake, ambos criptografados.
4. O cliente verifica o certificado e manda a sua própria mensagem `Finished`.
5. Só então a primeira requisição HTTP (`GET /`, cabeçalhos, `User-Agent`) viaja, dentro do canal criptografado.

Para o fingerprint, o que importa é o passo um. O `ClientHello` é enviado antes de existir qualquer chave compartilhada, então vai **sem criptografia**; o servidor, o CDN à frente dele e cada equipamento de rede no caminho conseguem lê-lo como está, antes de qualquer cabeçalho, cookie ou linha de JavaScript.

## Quais campos a mensagem ClientHello contém?

O `ClientHello` é uma lista em que o cliente diz «isto é o que eu sei falar». Estes são os campos usados no fingerprint:

| Campo | O que carrega? | Por que diferencia? |
|---|---|---|
| `legacy_version` | O campo de versão antigo. Por compatibilidade, clientes de TLS 1.3 ainda escrevem aqui o valor do TLS 1.2 (`771`) | Sozinho diz pouco; a versão real está em uma extensão |
| `cipher_suites` | Suítes de cifra suportadas, **em ordem de preferência** | Tanto a lista quanto a ordem variam de biblioteca para biblioteca |
| `extensions` | Os números de tipo das extensões: `server_name` (0), `supported_groups` (10), `signature_algorithms` (13), ALPN (16), `supported_versions` (43), `key_share` (51) e outras | Quais extensões existem, e em que ordem, é próprio de cada software |
| `supported_groups` | Curvas e grupos utilizáveis na troca de chaves (`29` = x25519, `23` = secp256r1) | Grupos novos chegam primeiro aos navegadores |
| `ec_point_formats` | Formatos de ponto de curva elíptica | Algumas bibliotecas enviam três valores, outras só um |
| `signature_algorithms` | Algoritmos de assinatura aceitos, em ordem | O JA3 não usa, o JA4 usa |
| ALPN | O protocolo de aplicação que o cliente quer falar (`h2`, `http/1.1`) | Um navegador pede `h2`; clientes simples quase nunca pedem |

Quem preenche essas listas não é a sua aplicação, e sim a **biblioteca TLS** por baixo dela. O Chrome usa BoringSSL e o Firefox usa NSS. O módulo `ssl` do Python e o Node.js são construídos sobre OpenSSL. O curl que vem com o Windows usa Schannel, a pilha TLS do próprio sistema operacional. Cada biblioteca tem uma lista de cifras padrão, um conjunto de extensões e uma ordenação diferentes; é assim que a identidade do cliente vaza para o primeiro pacote do handshake.

## O que é um fingerprint TLS?

Um fingerprint TLS são essas listas do `ClientHello` reduzidas a uma string curta e comparável. Três propriedades o separam de outros sinais:

- **Ele é passivo.** O site não faz o cliente executar nada; apenas lê o primeiro pacote que chega. Desligar o JavaScript ou apagar os cookies não muda o resultado.
- **Ele independe dos cabeçalhos.** O `User-Agent` é uma linha de texto e muda com uma linha de código em qualquer biblioteca HTTP. O `ClientHello` vem do comportamento compilado da biblioteca.
- **Ele identifica o software, não a pessoa.** Todo mundo que roda a mesma versão do Chrome no mesmo sistema operacional produz o mesmo valor. A camada de JavaScript (canvas, fontes, WebGL), que tenta separar dispositivos um a um, está em [O que é fingerprint do navegador?](/pt-br/blog/browser-fingerprinting).

O método não foi inventado para detectar bots. O primeiro uso foi segurança de rede: malware criptografa o tráfego, mas a biblioteca TLS e as configurações costumam ser fixas. Um time de segurança que não enxerga o conteúdo ainda reconhece a mesma família de software pelo desenho do `ClientHello`.

## O que é o JA3 e como ele é calculado?

O JA3 é o primeiro método difundido que amarrou essa ideia a um formato padrão. Foi desenvolvido na Salesforce em 2017 por John Althouse, Jeff Atkinson e Josh Atkins e publicado como código aberto. Como descreve o [repositório JA3 da Salesforce](https://github.com/salesforce/ja3), o cálculo tem cinco passos:

1. Da mensagem `ClientHello` são tirados cinco campos: versão do TLS, suítes de cifra, extensões, curvas (`supported_groups`) e formatos de ponto de curva elíptica.
2. Os valores de cada campo são convertidos para **números decimais** e juntados com `-` na ordem em que aparecem na mensagem.
3. Os cinco campos são unidos com `,`. Se um campo estiver vazio, o lugar dele fica vazio.
4. Valores GREASE (explicamos abaixo) não entram nas listas.
5. Tira-se o hash MD5 da string resultante. Esse hash de 32 caracteres é o fingerprint JA3.

O exemplo do próprio repositório é este:

```text
769,47-53-5-10-49161-49162-49171-49172-50-56-19-4,0-10-11,23-24-25,0
→ ada70206e40642a3e4461f35503241d5
```

Lendo da esquerda: `769` significa TLS 1.0; depois vêm doze suítes de cifra, três extensões, três curvas e um único formato de ponto. Aqui o MD5 não serve para segurança, e sim para transformar uma string longa em uma chave pesquisável de tamanho fixo.

Duas observações: como clientes de TLS 1.3 escrevem o valor do TLS 1.2 no campo de versão, quase toda string JA3 atual começa com `771`. A Salesforce arquivou o repositório em 1 de maio de 2025; ferramentas como o Wireshark continuam calculando o valor, mas o método não recebe mais manutenção.

## O que é GREASE e por que o JA3 o ignora?

GREASE é um mecanismo de robustez definido na [RFC 8701](https://www.rfc-editor.org/rfc/rfc8701). O cliente acrescenta às suas listas de suítes de cifra, extensões e grupos alguns valores reservados escolhidos ao acaso, no padrão `0x0A0A`, `0x1A1A` … `0xFAFA`. Esses valores não significam nada. O objetivo é testar sem parar se os servidores ignoram em silêncio valores que não reconhecem: um servidor defeituoso que derruba a conexão diante de um valor desconhecido é notado hoje, e não no dia em que um recurso novo do TLS entrar no ar.

Como os valores são sorteados, o mesmo navegador produziria um hash diferente a cada conexão se o JA3 os contasse. Por isso a documentação do método pede que os valores GREASE sejam pulados; no código abaixo, quem faz isso é a função `is_grease`.

## Por que o Chrome embaralha a ordem das extensões?

O GREASE protege o conteúdo das listas, não a ordem delas. O fato de softwares de servidor e de equipamentos intermediários confiarem na **ordem fixa de extensões** do Chrome trazia o mesmo risco, então o time do Chrome tornou a ordem aleatória também. Segundo [o registro no Chrome Platform Status](https://chromestatus.com/feature/5124606246518784), o recurso «TLS ClientHello extension permutation» passou a vir ligado por padrão no Chrome 110. A justificativa registrada não é fugir do fingerprint, e sim evitar que o ecossistema fique frágil: uma ordem fixa leva quem desenvolve servidores a reconhecer o Chrome e supor um comportamento específico, o que dificulta mudanças futuras no TLS. A RFC 8446 já diz que as extensões podem vir em qualquer ordem; a única exceção é a `pre_shared_key`, que precisa ficar por último quando está presente.

Como o JA3 junta as extensões na ordem da mensagem, essa mudança deixou instável o hash JA3 dos navegadores baseados em Chromium. Medimos com o ouvinte mais abaixo: 24 conexões seguidas de um navegador baseado em Chromium deram **24 hashes JA3 diferentes**. Ao ordenar os números das extensões, todas tinham o mesmo conjunto; só a ordem mudava.

## O que é o JA4 e qual a diferença para o JA3?

O JA4 é o formato mais novo, publicado pela FoxIO, que responde a esse problema. Segundo [a especificação técnica do JA4](https://github.com/FoxIO-LLC/ja4/blob/main/technical_details/JA4.md), o fingerprint tem três partes no formato `a_b_c`. O exemplo do documento, `t13d1516h2_8daaf6152771_e5627efa2ab1`, se lê assim:

- **`t`**: TLS sobre TCP (`q` seria QUIC e `d`, DTLS).
- **`13`**: TLS 1.3. O JA4 lê a versão na extensão `supported_versions`, não no campo antigo.
- **`d`**: existe extensão SNI, ou seja, o cliente está se conectando a um nome de domínio (`i` mostra que não há SNI).
- **`15`** e **`16`**: 15 suítes de cifra e 16 extensões, sem contar GREASE.
- **`h2`**: o primeiro e o último caractere do primeiro valor da lista ALPN, isto é, HTTP/2.
- **`8daaf6152771`**: os códigos hexadecimais das suítes de cifra são **ordenados**, passam por SHA-256 e ficam os 12 primeiros caracteres.
- **`e5627efa2ab1`**: os códigos de extensão são ordenados (sem SNI e ALPN), os algoritmos de assinatura são anexados na ordem original e o resultado é resumido do mesmo jeito.

A ordenação tira o efeito do embaralhamento das extensões. A primeira parte, legível, permite uma separação grosseira sem olhar os hashes: duas primeiras partes, uma terminando em `h2` e a outra em `00` por não enviar ALPN, não vêm do mesmo software.

| | JA3 | JA4 |
|---|---|---|
| Quem publica | Salesforce (2017), repositório arquivado em 2025 | FoxIO, com desenvolvimento ativo |
| Formato | um único hash MD5 de 32 caracteres | `a_b_c`: prefixo legível + dois hashes SHA-256 encurtados |
| Ordem das extensões | a da mensagem | ordenada; não sofre com o embaralhamento |
| Versão do TLS | campo de versão antigo (`771` mesmo no TLS 1.3) | extensão `supported_versions` |
| ALPN, SNI | aparecem só como número de extensão | os dois vêm escritos na primeira parte |
| Algoritmos de assinatura | não usados | entram na terceira parte |
| GREASE | ignorado | ignorado |

Segundo a nota de licença no repositório da FoxIO, o JA4, o fingerprint de cliente TLS, é aberto sob licença BSD 3-Clause; os demais membros da família (JA4S, JA4H, JA4X, JA4T e por aí vai) seguem uma licença própria da FoxIO.

## Um proxy muda o fingerprint TLS?

Não. O motivo está em como um proxy transporta o tráfego HTTPS.

No proxy HTTP, o cliente primeiro envia ao proxy uma requisição `CONNECT destino.com:443`. O proxy abre uma conexão TCP até o destino, responde `200 Connection Established` e a partir daí só repassa bytes. O `ClientHello` viaja **dentro** desse túnel: quem o produz é o seu cliente, quem o lê é o site de destino, e o proxy não muda o conteúdo nem o reescreve. No SOCKS5 é igual; o protocolo repassa a conexão TCP uma camada abaixo e nem sabe que os dados que carrega são TLS. Percorremos os dois protocolos passo a passo em [Diferença entre SOCKS e HTTP proxy](/pt-br/blog/socks-vs-http-proxy).

O resultado é que o site vê ao mesmo tempo o endereço IP do proxy e o fingerprint TLS do **seu cliente**. Usar [Proxies HTTPS](https://proxynet.io/pt-br/https-proxy) ou [Proxies SOCKS5](https://proxynet.io/pt-br/socks5-proxy) não muda isso, e o proxy ser residencial, móvel ou de datacenter também não. Testamos com um proxy de teste local: o mesmo cliente curl se conectou ao ouvinte abaixo de forma direta, por um túnel HTTP `CONNECT` e por SOCKS5, e o ouvinte registrou a mesma string JA3 nas três vezes.

O único caso em que o fingerprint muda é quando há no meio um intermediário que **encerra** o TLS:

| O que está no meio | Quem estabelece o TLS com o destino? | Qual fingerprint o site vê? |
|---|---|---|
| Proxy HTTP (túnel `CONNECT`) | Seu cliente | O do seu cliente |
| Proxy SOCKS5 | Seu cliente | O do seu cliente |
| VPN | Seu cliente | O do seu cliente |
| Gateway corporativo com inspeção TLS | O gateway | O do gateway |
| Antivírus com varredura HTTPS ligada | O antivírus | O do antivírus |
| Serviço que baixa a página no seu lugar | O cliente do serviço | O do cliente do serviço |

A quinta linha aconteceu na nossa própria máquina enquanto preparávamos este texto: o curl no Windows devolvia um fingerprint ao se conectar direto ao serviço de eco e outro ao passar pelo túnel do proxy local. A causa era a varredura HTTPS do antivírus, que refazia a conexão direta com a pilha TLS dele. Se o valor no serviço de eco não parece com a biblioteca que você esperava, veja quem assinou o certificado.

## Como ver a sua própria string JA3?

O caminho mais curto é um serviço de eco: abra [a página TLS do BrowserLeaks](https://browserleaks.com/tls) no navegador e você verá seus valores JA3 e JA4. Se quiser olhar no nível de pacote, o Wireshark calcula esses valores sozinho nos pacotes `ClientHello`; na [referência de filtros de exibição](https://www.wireshark.org/docs/dfref/t/tls.html) os campos aparecem como `tls.handshake.ja3` e `tls.handshake.ja4`.

Para ver o cálculo em si, você pode usar o script abaixo. Ele roda só com a biblioteca padrão do Python e não envia nada para fora: abre um ouvinte TCP cru na porta `8443`, analisa a mensagem `ClientHello` de cada cliente que se conecta e imprime a string JA3 e o hash dela. Na inicialização ele conecta uma vez o próprio cliente `urllib` do Python. O ouvinte não completa o handshake, lê o primeiro pacote e fecha; um erro de conexão do lado do cliente é o esperado.

```python
import hashlib
import socket
import threading
import urllib.request

HOST, PORT = "127.0.0.1", 8443

def is_grease(value):
    # RFC 8701: 0x0a0a, 0x1a1a, ... 0xfafa
    return (value & 0x0F0F) == 0x0A0A and (value >> 8) == (value & 0xFF)

def read_client_hello(conn):
    data = b""
    while len(data) < 5:
        data += conn.recv(4096)
    if data[0] != 22:  # 22 = registro de handshake
        raise ValueError("nao e um handshake TLS")
    record_length = int.from_bytes(data[3:5], "big")
    while len(data) < 5 + record_length:
        chunk = conn.recv(4096)
        if not chunk:
            break
        data += chunk
    return data[5 : 5 + record_length]

def ja3_from_client_hello(hello):
    if hello[0] != 1:  # 1 = ClientHello
        raise ValueError("nao e um ClientHello")
    pos = 4  # tipo da mensagem (1) + tamanho (3)
    version = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2 + 32  # versao + random
    pos += 1 + hello[pos]  # session_id
    size = int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2
    ciphers = [int.from_bytes(hello[i : i + 2], "big") for i in range(pos, pos + size, 2)]
    pos += size
    pos += 1 + hello[pos]  # compression_methods
    end = pos + 2 + int.from_bytes(hello[pos : pos + 2], "big")
    pos += 2

    extensions, groups, point_formats = [], [], []
    while pos < end:
        ext_type = int.from_bytes(hello[pos : pos + 2], "big")
        ext_size = int.from_bytes(hello[pos + 2 : pos + 4], "big")
        body = hello[pos + 4 : pos + 4 + ext_size]
        pos += 4 + ext_size
        extensions.append(ext_type)
        if ext_type == 10:  # supported_groups
            groups = [int.from_bytes(body[i : i + 2], "big") for i in range(2, len(body), 2)]
        elif ext_type == 11:  # ec_point_formats
            point_formats = list(body[1:])

    def join(values):
        return "-".join(str(v) for v in values if not is_grease(v))

    fields = [str(version), join(ciphers), join(extensions), join(groups), join(point_formats)]
    ja3_text = ",".join(fields)
    return ja3_text, hashlib.md5(ja3_text.encode()).hexdigest()

def own_python_client():
    try:
        urllib.request.urlopen(f"https://localhost:{PORT}", timeout=5)
    except OSError:
        pass  # o ouvinte fecha sem responder, e o esperado

def main():
    server = socket.create_server((HOST, PORT))
    print(f"ouvindo em https://localhost:{PORT}, Ctrl+C para sair")
    threading.Thread(target=own_python_client, daemon=True).start()
    while True:
        conn, _ = server.accept()
        with conn:
            try:
                ja3_text, ja3_hash = ja3_from_client_hello(read_client_hello(conn))
            except (ValueError, IndexError, OSError) as exc:
                print("nao foi possivel ler:", exc)
                continue
        print(ja3_hash, ja3_text)

if __name__ == "__main__":
    main()
```

Com o script rodando, aponte clientes diferentes para o mesmo endereço a partir de outro terminal. Escreva `localhost` no endereço; um cliente que se conecta a um endereço IP não envia a extensão SNI e o fingerprint muda.

```bash
curl -k https://localhost:8443
node -e "require('https').get('https://localhost:8443', { rejectUnauthorized: false }).on('error', () => {})"
```

Na nossa máquina (Windows 11, Python 3.13 com OpenSSL 3.0, Node.js 24 e curl 8 compilado com Schannel) a saída foi esta. Encurtamos o campo das suítes de cifra para melhorar a leitura:

```text
331a436afb23d4e31134c11b301bdcb5 771,4866-4867-4865-…,0-11-10-35-16-22-23-49-13-43-45-51-21,29-23-30-25-24-256-257-258-259-260,0-1-2
2e6c64f66822fc35b6a7a128b557f1de 771,4866-4865-49196-…,0-43-13-35-10-11-16-51-49-23-65281-45,29-23-24,0
944d1e1858cd278718f8a46b65d3212f 771,4866-4867-4865-…,65281-0-11-10-35-22-23-13-43-45-51,4588-29-23-30-24-25-256-257,0-1-2
```

A mesma máquina, o mesmo IP e três identidades distintas: na ordem, Python, curl e Node.js. Para o Python, o serviço de eco devolveu exatamente o mesmo hash, ou seja, o analisador funciona. O `4588` na linha do Node.js é o grupo híbrido pós-quântico de troca de chaves que aparece nos registros da IANA como `X25519MLKEM768`; esta versão do Python não o envia. No mesmo Node.js, `https.get` e o `fetch` embutido também deram valores JA3 diferentes, porque o `fetch` acrescenta a extensão ALPN: o fingerprint depende da biblioteca HTTP usada, não da linguagem. As diferenças entre bibliotecas do lado do Python nós comparamos em [Comparativo entre HTTPX, Requests e AIOHTTP](/pt-br/blog/httpx-vs-requests-vs-aiohttp).

Para medir o efeito de um proxy o ouvinte não serve (um proxy remoto não alcança o seu `localhost`), então pergunte duas vezes ao serviço de eco:

```python
import json
import urllib.request

PROXY = "http://user:pass@pr.proxynet.io:8000"
URL = "https://tls.browserleaks.com/json"

def fingerprint(opener):
    with opener.open(URL, timeout=15) as response:
        result = json.load(response)
    return result["ja3_hash"], result["ja4"]

direct = urllib.request.build_opener(urllib.request.ProxyHandler({}))
proxied = urllib.request.build_opener(urllib.request.ProxyHandler({"https": PROXY}))

print("direto:", *fingerprint(direct))
print("proxy :", *fingerprint(proxied))
```

Você verá os mesmos valores nas duas linhas; o que muda é só o endereço IP que o serviço enxerga.

## Como o dono de um site usa esse dado?

Produtos de CDN e firewall expõem o valor JA3 ou JA4 como um campo ao lado de cada requisição. O dono do site usa esse campo ao escrever regras e ao revisar os registros. Usos típicos:

- **Verificação de coerência.** Se o `User-Agent` diz «Chrome» mas o fingerprint não bate com nenhuma versão conhecida do Chrome, o cabeçalho foi alterado. Sozinho não é prova definitiva, mas é um sinal forte que pesa na pontuação. O cabeçalho em si está em [O que é User-Agent?](/pt-br/blog/what-is-user-agent).
- **Limite de requisições independente do IP.** Se um tráfego espalhado por centenas de IPs carrega o mesmo fingerprint, o contador pode ser amarrado ao fingerprint em vez do IP. Um IP rotativo não zera esse contador.
- **Reconhecer ferramentas conhecidas.** Existem listas de fingerprints compiladas para famílias de malware e ferramentas de varredura; times de segurança as cruzam com os registros.

Também há limites. Um site que bloqueia o valor de um navegador atual bloqueia todos os visitantes que usam esse navegador. Atualizações do navegador mudam o valor, e gateways corporativos e antivírus colocam os fingerprints deles no meio. Por isso um fingerprint TLS não decide nada sozinho; é um dos sinais que alimentam a pontuação junto da reputação do IP e do comportamento. Essa pontuação como um todo está em [Como funciona a detecção de bots?](/pt-br/blog/how-bot-detection-works), e as camadas da Cloudflare em [O que é o Cloudflare Precursor?](/pt-br/blog/cloudflare-precursor).

## O que isso significa para quem coleta dados?

Voltando à situação da introdução. O cliente Python diz na linha `User-Agent` que é Chrome, enquanto o `ClientHello` dele diz que é OpenSSL. O site vê a contradição no primeiro pacote. A reação certa é eliminar a contradição, não tentar escondê-la:

- **Não diga que é um navegador que você não é.** Deixe o `User-Agent` do seu script apresentar o script: um nome, uma versão e um endereço de contato. Um cliente que chega com identidade honesta, respeita o `robots.txt` e trabalha devagar pode ser reconhecido pelo dono do site e entrar numa lista de permissões. As regras estão em [O que é robots.txt e como ler o arquivo?](/pt-br/blog/robots-txt).
- **Se existe uma API oficial, use.** Uma requisição que chega com chave de API já tem identidade conhecida; a discussão sobre fingerprint some.
- **Se a página realmente precisa de um navegador, use um de verdade.** Quando você abre uma página desenhada por JavaScript com o Playwright dentro de um Chromium real, o seu cliente é mesmo aquele navegador; cabeçalhos, camada TLS e ambiente JavaScript concordam entre si. A instalação está em [O que é Playwright e como usá-lo com proxy](/pt-br/blog/playwright-proxy).
- **Reduza o ritmo e peça permissão.** Para um trabalho regular e de grande volume, escrever ao dono do site costuma ser a solução mais duradoura.

Os outros motivos de bloqueio, e os caminhos legítimos para eles, estão reunidos em [Como fazer web scraping sem ser bloqueado](/pt-br/blog/web-scraping-without-getting-blocked).

## Casos de uso

- **Diagnosticar um `403` no scraper:** se com o mesmo IP o navegador passa e o script não, a diferença está quase certamente na identidade do cliente. Os códigos de status separamos em [Códigos de status HTTP no web scraping](/pt-br/blog/http-status-codes-web-scraping), e o desenho geral está na nossa página de [solução de extração de dados](/pt-br/data-scraping).
- **Separar o tráfego de bots no seu próprio site:** agrupar os registros por fingerprint mostra o tráfego distribuído que o agrupamento por IP deixa passar.
- **Apresentar o seu próprio crawler:** uma versão fixa de biblioteca produz um fingerprint constante, e é assim que o seu crawler é reconhecido nos registros. A parte de infraestrutura está na nossa página de [solução de web crawler](/pt-br/web-crawler).
- **Acertar a expectativa sobre um proxy:** [Proxies residenciais](https://proxynet.io/pt-br/residential-proxy) muda a reputação do IP e a localização; a identidade do seu cliente continua sendo responsabilidade sua.

## Erros comuns

- **Achar que o cliente muda quando o `User-Agent` muda.** O cabeçalho é texto; o `ClientHello` é o comportamento da biblioteca e sai antes do cabeçalho.
- **Esperar que o proxy mude o fingerprint TLS.** Um proxy que faz túnel muda o IP e não encosta no handshake.
- **Esperar que o hash JA3 fique fixo em um navegador baseado em Chromium.** A ordem das extensões muda a cada conexão; para comparar, use JA4 ou uma lista de extensões ordenada.
- **Aceitar o resultado do serviço de eco sem questionar.** Um antivírus ou gateway corporativo que varre HTTPS mostra ao serviço o fingerprint dele mesmo.
- **Usar `127.0.0.1` na medição.** Não vai SNI, e o valor sai diferente do das conexões reais.

## Guia de decisão

| Situação | Recomendação |
|---|---|
| O IP muda pelo proxy e mesmo assim vem `403` na primeira requisição | Olhe a identidade do cliente: o `User-Agent` e a biblioteca que você usa dizem a mesma coisa? |
| Você quer saber o valor JA3 ou JA4 do seu próprio cliente | Um serviço de eco ou o ouvinte local acima |
| Você quer agrupar o tráfego do Chrome nos seus registros | JA4, não JA3; não sofre com o embaralhamento de extensões |
| A página precisa de JavaScript e de um navegador real | Automação com navegador real (Playwright), ritmo razoável e o escopo que o site permite |
| O serviço de eco mostra um valor inesperado | Veja quem assinou o certificado; pode haver software encerrando o TLS no meio |
| Seu site recebe tráfego de bots distribuído | Amarre o limite de requisições ao par IP + fingerprint em vez do IP; não bloqueie por um único hash |

## Perguntas frequentes

### Qual a diferença entre JA3 e JA3S?

O JA3 é produzido a partir da mensagem `ClientHello` do cliente, e o JA3S a partir da mensagem `ServerHello` do servidor. Como um servidor responde de forma diferente a clientes diferentes, o JA3S não identifica o servidor sozinho; já a resposta dele ao mesmo cliente é sempre a mesma. Por isso times de segurança usam os dois em par.

### Usar uma VPN muda o fingerprint TLS?

Não. A VPN leva o tráfego por um túnel criptografado, mas o handshake TLS com o site continua sendo feito pelo seu navegador ou pelo seu script. O site vê o endereço IP do servidor VPN e o fingerprint do seu cliente. A diferença entre as duas ferramentas está em [Diferença entre proxy e VPN](/pt-br/blog/proxy-vs-vpn).

### Um fingerprint TLS me identifica pessoalmente?

Sozinho, não. Todo mundo que roda a mesma versão de navegador no mesmo sistema operacional produz o mesmo valor. Uma identidade que chega perto de uma pessoa nasce da combinação desse valor com o endereço IP, os cookies e os sinais da camada de JavaScript.

### Atualizar o navegador muda o fingerprint?

Pode mudar. Quando uma versão nova remove uma suíte de cifra ou acrescenta uma extensão, a lista e o hash mudam. Por isso as listas de fingerprints são mantidas versão a versão.

### Uma janela anônima ou apagar os cookies afeta o fingerprint TLS?

Não. A mensagem `ClientHello` é produzida pela biblioteca TLS do navegador; histórico, cookies e tipo de janela não entram nessas listas.

### O que faço se estou sendo bloqueado por causa do meu fingerprint?

Meça primeiro: veja qual valor aparece no serviço de eco e se há software encerrando o TLS no meio. Se você escreve um script, declare a sua identidade com honestidade, reduza o ritmo e use a API oficial ou o canal de permissão do site. Se o bloqueio acontece com um navegador comum, o problema quase certamente está na reputação do IP, e não no fingerprint.

## Em resumo

O fingerprint TLS sai do `ClientHello`, o primeiro pacote, sem criptografia, de uma conexão criptografada. O JA3 junta na ordem as cinco listas dessa mensagem e tira o hash MD5; quando o Chrome passou a embaralhar a ordem das extensões, o hash ficou instável, e o JA4 resolveu isso ordenando as listas. O valor mostra o software, não a pessoa, sai antes dos cabeçalhos e nenhum proxy que faça túnel encosta nele: o proxy muda o IP e a identidade do cliente fica com você. Para somar o tipo de IP certo a uma identidade de cliente constante e honesta, dê uma olhada nos [nossos serviços de proxy](/pt-br/proxy).
