Pasaste de HTTP en crudo a un navegador real. Huella TLS: genuinamente la de Chrome. IP: residencial. Y el sitio aún lo sabe. En 2026, la detección de navegadores headless ha avanzado mucho más allá de navigator.webdriver — la generación actual de comprobaciones detecta el propio protocolo de automatización, y la mayoría de los plugins "stealth" parchean las señales de ayer mientras exhiben ruidosamente las de hoy.
Este artículo mapea lo que los sitios realmente comprueban ahora, aproximadamente en el orden en que lo comprueban, y qué ayuda de verdad en cada capa. Completa la trilogía de detección con la huella TLS JA3/JA4 (capa de red) y la reputación de IP y ASN (capa de dirección) — esta es la capa del navegador.
navigator.webdriver — la especificación exige que sea true bajo automatización. Las configuraciones stealth de Playwright/Puppeteer lo ocultan; el --disable-blink-features=AutomationControlled de Chrome lo gestiona de forma nativa. Los sitios lo siguen comprobando porque es gratis y caza bots ingenuos.HeadlessChrome/ en el User-Agent. Arreglado por el nuevo modo headless (headless=new), que usa la ruta del binario real de Chrome.navigator.plugins, sin chrome.runtime, outerWidth 0×0. El modo headless antiguo fallaba todos estos; el nuevo headless en su mayoría los pasa.Si un objetivo solo comprueba el Nivel 1, Playwright moderno con headless=new pasa sin problemas. Los objetivos serios avanzaron hace años.
Playwright y Puppeteer manejan Chrome a través del Chrome DevTools Protocol, y CDP deja efectos secundarios en tiempo de ejecución que el JavaScript de la página puede observar:
// The classic CDP leak: serialization callbacks
const err = new Error();
Object.defineProperty(err, "stack", {
get() {
// This getter fires DURING console serialization -
// which only happens when DevTools/CDP is attached
window.__cdp_detected = true;
},
});
console.debug(err);
Variantes de esto — efectos secundarios de getters durante la serialización de console, anomalías de tiempo en Runtime.evaluate, el comportamiento de toString() sobre funciones nativas parcheadas — están en todo bundle antibot comercial. El punto crucial: esto detecta el protocolo, no el modo headless. Un Chrome con cabeza (headed), parcheado para stealth y de aspecto perfectamente humano sigue fallando si se maneja a través de CDP.
Qué ayuda:
Más allá de los artefactos, los sistemas modernos comprueban si la historia de tu navegador es internamente consistente:
| Comprobación | Indicio de bot |
|---|---|
| Hash de render de Canvas / WebGL | Renderizado por software SwiftShader/llvmpipe = GPU de servidor; o un hash compartido por 10.000 "usuarios distintos" |
| Fuentes y códecs | Conjunto de fuentes de servidor Linux bajo un User-Agent de Windows |
| Zona horaria × configuración regional × geo de IP | Intl dice UTC, la IP dice Texas, Accept-Language dice de-DE |
| Métricas de pantalla | 1920×1080 sin barra de tareas, devicePixelRatio exactamente 1, ventana nunca redimensionada |
| Concurrencia de hardware / memoria | 96 núcleos reportados a una página que dice ser un teléfono |
| Microseñales conductuales | Sin entropía de ratón, rellenos de formulario instantáneos, scrolls en pasos idénticos de 100px |
Aquí es donde muere el stealth casero: cada parche que aplicas debe concordar con todas las demás señales. Un UA de Windows falseado en un contenedor Linux contradice simultáneamente las fuentes, las cadenas de GPU y las huellas TCP. La coherencia le gana a la perfección — un Chrome con cabeza honesto sobre Linux puntúa mejor que un "Windows" mal falseado.
Nada de esto importa si la capa de dirección está quemada: un navegador impecable sobre un ASN de centro de datos sigue siendo "un navegador impecable en un centro de datos" — y los umbrales de detección de headless son adaptativos a la reputación de IP. El mismo navegador recibe más challenges de JavaScript desde una IP marcada. Las salidas residenciales reducen efectivamente el escrutinio que la capa del navegador tiene que sobrevivir:
browser = p.chromium.launch(
headless=False, # headed survives more checks
args=["--disable-blink-features=AutomationControlled"],
proxy={
"server": "us.jibaoproxy.com:913",
"username": "USERNAME", "password": "PASSWORD",
},
)
toString() sin parchear es en sí mismo una señal. Ganan los parches escasos y coherentes.IP residenciales que concuerdan con la historia que cuenta tu huella — 500MB de tráfico gratis, sin tarjeta requerida.
Empezar prueba gratisLos nuevos usuarios reciben 500MB al registrarse, más un bono en la primera recarga. Oferta por tiempo limitado.