Has comprado proxies residenciales, has configurado un nuevo User-Agent de Chrome, y el sitio aún devuelve 403 en la primera solicitud. ¿Te suena familiar? El problema no está en la IP ni en los encabezados. Te han detectado antes de que el servidor haya leído siquiera un encabezado HTTP, a través del apretón de manos TLS. En 2026, este es el vector de detección número 1, y un simple requests lo falla automáticamente. Vamos a analizar cómo funciona y cómo solucionarlo con unas pocas líneas de código a través de curl_cffi.
Qué está pasando: te detecta el apretón de manos TLS
Cuando un cliente establece una conexión HTTPS, primero envía un paquete ClientHello, incluso antes de cualquier HTTP. En él se enumeran: la versión de TLS, la lista de conjuntos de cifrado soportados (cipher suites), extensiones de TLS (SNI, ALPN, supported_groups), curvas elípticas y formatos de puntos. El orden y la composición de estos campos varían entre diferentes clientes y se pueden identificar antes de que el cliente pronuncie una sola palabra.
De estos campos se calcula una huella digital. JA3 (estándar de 2017) toma una cadena del tipo TLSVersion,Ciphers,Extensions,EllipticCurves,ECPointFormats y la hash MD5, obteniendo una firma de 32 caracteres. El problema de JA3 es que desde enero de 2023, Chrome aleatoriza el orden de las extensiones: 16 extensiones dan 16! (más de 20 billones) de combinaciones, y el mismo navegador produce diferentes JA3.
Por lo tanto, la industria ha pasado a JA4 (FoxIO, implementación masiva 2024–2025). JA4 ordena los códigos de extensión por valor hexadecimal antes de la hash, por lo que la aleatorización de Chrome ya no lo rompe. La hash es un SHA-256 truncado, el formato es legible y de tres partes (a_b_c), e incluye ALPN y soporte para QUIC/HTTP3. Por ejemplo: Chrome 124 produce t13d1516h2 (15 cifrados, 16 extensiones, ALPN h2), mientras que Python puro requests genera t13d1715h2. Para un anti-bot, la segunda firma es un marcador directo de "esto es un script".
Por qué en 2026 no puedes prescindir de esto
La detección JA4 está integrada en todos los grandes proveedores: Cloudflare compara la huella contra listas permitidas, Akamai añade un hash separado para los marcos de configuración HTTP/2, DataDome compara con una base de datos de bots conocidos. La lógica es simple y letal: si envías User-Agent: Chrome 131, pero la huella TLS grita "urllib3/OpenSSL", hay un desincronización, y te bloquean de inmediato. Ningún proxy te salva: una IP residencial perfecta con la huella de Python requests aún pierde.
Por esta razón, la combinación de "proxy + falsificación de huella" se ha convertido en 2026 en una higiene básica del scraping, y no en una opción para avanzados.
Solución: curl_cffi en 5 minutos
curl_cffi es un envoltorio de Python sobre curl-impersonate (curl modificado, construido con BoringSSL de Chrome o NSS de Firefox en lugar de OpenSSL). Reproduce un apretón de manos de navegador auténtico y, además, tiene una API casi idéntica a la familiar requests.
Paso 1. Instalación. Los binarios de curl-impersonate para Windows/macOS/Linux se descargan automáticamente:
pip install curl-cffi
Paso 2. Solicitud básica. Cambias la importación y añades un parámetro:
from curl_cffi import requests
resp = requests.get("https://target.com/", impersonate="chrome")
print(resp.status_code)
print(resp.http_version) # HTTP/2 — como en un navegador real
Una línea impersonate="chrome" falsifica inmediatamente cuatro capas: la huella TLS (JA3/JA4), la versión HTTP (HTTP/2 en lugar de HTTP/1.1), el orden de los encabezados y las negociaciones ALPN.
Paso 3. Siempre usa el alias genérico, no fijes la versión. Escribe impersonate="chrome" (o "safari", "safari_ios") — el alias se resolverá automáticamente en el perfil más reciente. Un impersonate="chrome124" se volverá obsoleto: Chrome se actualiza cada ~4 semanas, y el perfil antiguo se convertirá en una anomalía. Los objetivos confiables son Chrome, Edge y Safari/iOS (perfiles de chrome99 a chrome131, safari15–18).
Paso 4. Proxies y sesiones. Para un scraping real, mantén el estado en una sesión y conecta proxies. Una IP residencial o móvil es obligatoria aquí: la IP de centro de datos se detecta por ASN, independientemente de TLS:
from curl_cffi import requests
session = requests.Session(impersonate="chrome")
headers = {
"Accept-Language": "en-US,en;q=0.9",
"Accept-Encoding": "gzip, deflate, br",
"Referer": "https://www.google.com/",
}
proxies = {
"http": "http://user:pass@proxy-host:port",
"https": "http://user:pass@proxy-host:port",
}
resp = session.get("https://target.com", headers=headers, proxies=proxies)
Paso 5. Asincronía para volumen. A diferencia de requests, curl_cffi tiene async y HTTP/2 de forma nativa:
import asyncio
from curl_cffi.requests import AsyncSession
async def fetch(session, url):
r = await session.get(url, impersonate="chrome")
return r.status_code
async def main(urls):
async with AsyncSession() as session:
return await asyncio.gather(*[fetch(session, u) for u in urls])
asyncio.run(main(["https://target.com"] * 20))
Verifica tu huella — no adivines
Antes de enviar tráfico real, asegúrate de que la falsificación realmente funcione. Envía una solicitud a verificadores públicos y compara JA4 con el de un navegador de referencia:
- tls.peet.ws — devuelve JA3, JA4, huella de Akamai y marcos HTTP/2 en JSON. Solicítalo a través de
curl_cffiy a través de un Chrome real, y compara los hashes. - ja4db.com — base de datos de JA4 conocidos, ayuda a entender a quién te pareces.
- browserleaks.com/tls y la herramienta JA3/JA4 de Scrapfly — desglose detallado de los campos.
En staging, es conveniente colocar mitmproxy entre el scraper y el objetivo y monitorear el hash JA4 real de cada solicitud.
Diagnóstico: ¿sigues recibiendo 403/429?
Si la huella es correcta y los bloqueos persisten, sigue la lista de verificación de lo más común a lo menos común:
- IP de centro de datos. Razón número 1. Cambia a proxies residenciales o móviles — por qué una sola huella no es suficiente se detalla en el artículo sobre detección de proxies residenciales a través de IP Intelligence.
- Perfil obsoleto.
pip install -U curl-cffiy alias genérico"chrome". - Tasa demasiado alta. Añade pausas aleatorias de 1 a 3 segundos entre solicitudes.
- Encabezados desnudos. Asegúrate de enviar
Accept-Language,Accept-Encoding,Referer— su ausencia también es una anomalía. - Desincronización entre sesión y IP. Regla: una sesión — una IP durante toda su vida útil.
- Estado 200 ≠ éxito. Verifica el cuerpo de la respuesta: bajo el código 200 puede haber una página con CAPTCHA.
Dónde se topa curl_cffi con un muro
curl_cffi cierra la capa de red — y eso es todo. No ejecuta JavaScript. Por lo tanto, es impotente contra desafíos JS: Cloudflare Turnstile, la página "Checking your browser…" (IUAM), la cookie cf_clearance que establece el script después de la verificación, todo esto requiere un entorno de navegador real. Por qué en 2026 los solucionadores de CAPTCHA casi han dejado de funcionar contra tales sistemas preventivos, lo hemos analizado en un análisis separado sobre eludir CAPTCHA.
Qué hacer cuando te topas con un muro de JS:
- Híbrido. Playwright o Nodriver ejecuta el desafío y obtiene
cf_clearance, luego la cookie se pasa a un rápidocurl_cffipara la mayoría de las solicitudes — así pagas por un navegador pesado una sola vez. - Servicios de solucionadores (CapSolver, 2Captcha) para la emisión automática de tokens.
- API de scraping gestionado, si no quieres mantener la infraestructura.
Y recuerda sobre la seguridad de los hilos: cada hilo debe tener su propia sesión. Fija la versión de curl-cffi en requirements.txt y revisa los perfiles cada 6 a 12 semanas, cuando los navegadores se actualicen.
Alternativas a curl_cffi
- tls-client — envoltorio sobre la biblioteca Go basada en uTLS, con perfiles (
chrome_124,safari_ios_17) y la banderarandom_tls_extension_order=True. Ajuste flexible y fino de la huella. - primp — cliente en Rust, permite establecer
impersonate_osde forma independiente y ofrece mayor capacidad; desventaja — la API no coincide completamente conrequestsy la biblioteca es más nueva.
Qué proxy necesitas y por qué
La falsificación de huellas y los proxies resuelven diferentes mitades de una misma tarea: curl_cffi aborda la cuestión de "cómo se ve la conexión", el proxy — "de dónde viene". Un anti-bot verifica ambas señales de forma independiente, por lo que un JA4 perfecto con un ASN de centro de datos negro es inútil. Para objetivos protegidos (marketplaces, redes sociales, agregadores de viajes), utiliza proxies residenciales o móviles: tienen un origen operativo limpio, y los móviles además se ocultan tras el efecto CGNAT "efecto de multitud". Deja el centro de datos para objetivos no sensibles y alto volumen.
Conclusión
En 2026, el scraping es un juego de identidades, no solo de IP. Un requests desnudo se presenta como un script a nivel de apretón de manos TLS y pierde antes de que se envíe el primer encabezado. Cambiar la importación a curl_cffi con impersonate="chrome" elimina esta falla en cinco minutos, pero solo funciona en combinación con una IP residencial o móvil limpia y con la comprensión del límite: la capa de red — sí, desafíos JavaScript — no. Construye tu stack de manera honesta: huella correcta, proxy correcto, híbrido con navegador donde haya un muro de JS — y el 403 en la primera solicitud quedará en el pasado.
