La huella TLS identifica el software que abre una conexión HTTPS observando cómo la abre — qué cifrados ofrece y en qué orden, qué extensiones y curvas elípticas lista — antes de que se envíe cualquier contenido de la solicitud. Cada biblioteca TLS y cada versión de navegador saluda a un servidor de manera ligeramente diferente, y ese saludo es visible para el servidor en texto plano aunque todo lo posterior esté cifrado. JA3 y su sucesor JA4 convierten el saludo en un hash corto. Los sistemas anti-bot comparan ese hash con lo que el navegador declarado debería haber producido. Esta es la razón por la que un script de Python que se anuncia como Chrome, conectándose a través de una IP residencial impecable, sigue siendo detectado: la IP decía “usuario doméstico en Berlín”; el handshake TLS decía “OpenSSL en un script”.
De dónde viene la huella: el ClientHello
Una conexión HTTPS comienza con un handshake TLS. El primer mensaje, enviado por el cliente, es el ClientHello. No está cifrado — debe serlo, porque el cifrado aún no se ha negociado — y contiene, entre otras cosas:
- la versión de TLS que soporta el cliente;
- la lista de suites de cifrado que está dispuesto a usar, en su orden de preferencia;
- una lista de extensiones — SNI (el nombre de host), ALPN (qué protocolos de aplicación, p. ej.
h2,http/1.1), grupos soportados (curvas elípticas), algoritmos de firma, key share, session ticket, etc. — de nuevo en un orden específico; - las curvas elípticas y formatos de punto que soporta.
Ninguno de estos valores es secreto y ninguno se elige por sitio; están integrados en la biblioteca TLS y en la aplicación que la usa. Chrome 128 envía un ClientHello particular; Firefox envía otro; el módulo ssl de Python (OpenSSL) envía un tercero; crypto/tls de Go un cuarto; curl un quinto. El servidor ve el ClientHello de cada conexión de todos modos — tiene que verlo para responderlo. Registrarlo y convertirlo en hash no cuesta nada.
JA3 y JA4: convertir el handshake en una firma
JA3
Introducido por investigadores de Salesforce en 2017, JA3 concatena cinco campos del ClientHello — versión TLS, suites de cifrado, extensiones, curvas elípticas, formatos de punto de curvas elípticas — como listas decimales separadas por comas, y luego aplica un hash MD5 a la cadena. El resultado es una huella de 32 caracteres como cd08e31494f9531f560d64c695473da9. Cualquiera puede calcularlo a partir de una captura de paquetes, y las listas que mapean hashes JA3 a clientes conocidos (“este hash = Chrome 120 en Windows”, “este hash = python-requests”) se comparten ampliamente.
JA3 tiene una debilidad que se convirtió en una característica para la evasión: como hashea la lista de extensiones en orden, y Chrome (desde la versión 110) aleatoriza el orden de las extensiones en cada conexión, una sola instalación de Chrome produce muchos hashes JA3 diferentes. Eso se hizo deliberadamente para desalentar a los servidores de fosilizarse en una sola huella — e hizo que JA3 fuera mucho menos útil para identificar navegadores.
JA4
JA4, lanzado por FoxIO en 2023, fue diseñado para sobrevivir a eso. Ordena las listas de cifrados y extensiones antes de aplicar el hash (para que la reorganización de Chrome ya no importe), mantiene algunos campos legibles para humanos, e incorpora más señales: si la conexión es sobre TCP o QUIC, el tipo de SNI, el valor de ALPN, la versión TLS y los recuentos de cifrados y extensiones. Una huella JA4 se ve así: t13d1516h2_8daaf6152771_e5627efa2ab1: el primer bloque dice “TLS 1.3 sobre TCP, 15 cifrados, 16 extensiones, ALPN h2”, los otros dos son hashes truncados de la lista ordenada de cifrados y de la lista ordenada de extensiones más algoritmos de firma. JA4 es en realidad una familia (JA4 para TLS, JA4H para cabeceras HTTP, JA4S para el lado del servidor, JA4X para certificados, y otros); cuando los proveedores anti-bot dicen “huella TLS” hoy en día suelen referirse a JA4 o a un equivalente propietario construido de la misma manera.
Por qué el proxy no puede ayudar
Un proxy retransmite el handshake TLS; no lo reescribe. Con un proxy HTTP, el cliente envía CONNECT host:443, el proxy abre un túnel TCP, y el propio ClientHello del cliente viaja a través del túnel hasta el destino sin modificaciones. Con SOCKS5 ocurre lo mismo una capa más abajo. Por lo tanto, el servidor de destino ve dos hechos independientes:
- la IP de origen — la del proxy, residencial e impecable;
- el ClientHello — el del cliente, que dice exactamente qué biblioteca lo construyó.
La lógica anti-bot entonces hace una pregunta simple: ¿coincide la huella TLS con el User-Agent? La cabecera User-Agent afirma “Chrome 128 en Windows”. El JA4 dice “OpenSSL 3.x vía Python”. El Chrome real nunca produce ese JA4. La solicitud se marca — no porque la IP sea mala, sino porque la historia es internamente inconsistente. Esta es la razón más común por la que un proxy residencial “limpio” sigue recibiendo CAPTCHAs en sitios protegidos, y por qué decimos que un proxy responde de dónde, no quién; ver qué hace y qué no oculta un proxy.
El proxy puede importar en un aspecto: algunos proveedores terminan TLS ellos mismos (un proxy “man-in-the-middle” que descifra y vuelve a cifrar, requiriendo que instales su certificado). En ese caso, el destino ve el ClientHello del proxy, no el tuyo — lo que suele ser peor, ya que es una biblioteca del lado del servidor, no un navegador. Los proxies ordinarios no hacen esto.
Cómo se ve cada tipo de cliente
| Cliente | Qué dice su huella TLS | ¿Consistente con un User-Agent de navegador? |
|---|---|---|
| Chrome / Firefox / Safari reales | Ese navegador y versión, en ese SO | Sí — es el navegador |
Python requests, httpx, aiohttp | OpenSSL configurado por el módulo ssl de Python | No — reconocible como script independientemente de las cabeceras |
Node.js fetch / axios | La compilación OpenSSL/BoringSSL de Node | No |
Go net/http | La pila TLS propia de Go; distintiva | No |
| curl (compilación predeterminada) | OpenSSL/LibreSSL según compilación | No |
| Chrome headless vía Puppeteer/Playwright | Chrome real — TLS correcto | Sí para TLS; otras señales (flags de automatización, plugins faltantes) pueden delatar |
| Clientes “imitadores” (curl-impersonate, curl_cffi, tls-client) | El ClientHello de un navegador elegido, reproducido byte a byte | Sí, si también igualas las cabeceras al mismo navegador |
Cómo tener una huella consistente
Hay tres rutas honestas, en orden creciente de coste.
- Usa un navegador real. Automatiza Chrome o Firefox con Playwright o Puppeteer; el TLS entonces es genuinamente del navegador. Más lento y pesado que HTTP puro, pero para sitios protegidos es la línea base. Empareja cada contexto de navegador con su propio proxy — ver proxies en Playwright, Puppeteer y Selenium.
- Usa un cliente HTTP imitador. Bibliotecas como curl_cffi (Python, construido sobre curl-impersonate) y tls-client reproducen el ClientHello y los ajustes HTTP/2 de navegadores específicos, de modo que obtienes velocidad de scraper con la huella de un navegador. Debes mantener todas las demás cabeceras consistentes con la misma versión del navegador — User-Agent,
Accept-Language, client hintssec-ch-ua— o intercambias una contradicción por otra. Nuestra guía de proxies en Python muestra el cambio derequestsacurl_cffi. - Deja de fingir. Si un objetivo ofrece una API oficial, úsala con tu identidad de cliente real. La huella TLS solo es un problema cuando contradice la afirmación.
Lo que no funciona: poner un User-Agent de Chrome en requests. Cambia una cabecera y deja el handshake intacto — el desajuste ahora es más agudo que antes, porque un UA de python-requests con una huella OpenSSL es al menos autoconsistente.
El panorama general: las huellas se apilan
TLS es una capa. Los proveedores anti-bot modernos también toman la huella de la conexión HTTP/2 (valores del frame SETTINGS, tamaños de ventana, orden de cabeceras y pseudo-cabeceras — a veces llamado huella de Akamai), la pila TCP/IP (tamaño de ventana, TTL, opciones; el clásico p0f), y por supuesto el navegador mismo a través de JavaScript, que un cliente HTTP no puede ejecutar en absoluto. Cada capa debe contar la misma historia que las demás y que la IP. Una IP residencial con huella TLS de navegador y ajustes HTTP/2 de navegador pero sin ejecución de JavaScript sigue siendo una anomalía en un sitio que espera que se ejecute el desafío JS. Esta es la razón por la que la respuesta honesta a “¿qué proxy evita la detección?” es “ninguno por sí solo” — y por qué nuestras clasificaciones ponderan lo que el proveedor hace más allá de la IP; ver la comparativa de proxies de élite.
Cómo ver tu propia huella
Varias páginas públicas muestran el JA3/JA4 que calculan para tu conexión, junto con qué navegador creen que es. Carga una desde un navegador real, luego haz una solicitud con tu script a través del mismo proxy, y compara. Si difieren, has encontrado la razón por la que tu script es tratado de manera diferente a tu navegador — y ningún cambio de proxy lo arreglará. Añade esto a las comprobaciones en Cómo probar un proxy siempre que el objetivo se sepa que usa anti-bot empresarial.
Preguntas frecuentes
¿Qué es JA3?
Un hash MD5 de cinco campos del ClientHello TLS (versión, cifrados, extensiones, curvas, formatos de punto) utilizado para identificar el software del cliente. Funciona bien para scripts y bibliotecas; se volvió poco fiable para Chrome una vez que Chrome comenzó a aleatorizar el orden de las extensiones.
¿Qué es JA4?
El sucesor de JA3: ordena las listas antes de aplicar el hash para que la reorganización de extensiones no lo cambie, mantiene parte de la huella legible, e incluye transporte (TCP/QUIC), ALPN y recuentos. Es la base de la mayoría de las detecciones actuales de huella TLS.
¿Un proxy residencial cambia mi huella TLS?
No. El proxy tuneliza tu handshake sin cambios; el destino ve la IP del proxy y tu ClientHello. Solo el software del cliente determina la huella.
¿Puedo falsificar la huella TLS de un navegador en Python?
Sí, con clientes imitadores como curl_cffi, que reproducen el ClientHello y el comportamiento HTTP/2 de un navegador elegido. Debes mantener las cabeceras consistentes con el mismo navegador. Las requests/httpx normales no pueden hacer esto.
¿Es la huella TLS lo mismo que la huella del navegador?
No. La huella TLS lee el handshake de la conexión cifrada e identifica el software del cliente; la huella del navegador ejecuta JavaScript e identifica el dispositivo (canvas, fuentes, GPU). Los sistemas anti-bot usan ambas, más la reputación de la IP, y verifican que coincidan.
¿Por qué el mismo script es bloqueado en un sitio y no en otro?
Porque las comprobaciones de huella TLS son una característica de los productos anti-bot empresariales, no de la web en general. Los sitios sin esa protección nunca miran el handshake; los que la tienen lo comparan con el User-Agent en cada solicitud.