Señales de navegador headless y con interfaz: método
Ejecuta el mismo fixture propio en sesiones con interfaz, headless y con perfil, y compara los tres registros en lugar de adivinar qué modo vio la página.
BotBrowser Team
Quieres la documentación estructurada de Despliegue?
Este artículo forma parte de la biblioteca editorial. Para pasos de configuración, material de referencia y actualizaciones continuas, entra en la sección de docs.
Los equipos preguntan si una página «ve» un navegador headless, y la respuesta honesta es que ve un conjunto de valores, algunos de los cuales difieren entre modos y otros no. Este artículo da un método para averiguar cuáles son cuáles, y va junto a la guía de protección de huella para recopilación de datos: aquella cubre qué debe mantener coherente una sesión de recopilación autorizada, y esta cubre cómo medir la diferencia entre modos antes de confiar en cualquiera de ellos.
BotBrowser puede ejecutar un perfil declarado en modo con interfaz y headless y mantener los valores visibles del navegador alineados con ese perfil, lo que permite una comparación a tres bandas sobre un fixture propio; no puede hacer que un navegador headless sea idéntico a uno con interfaz en todas las superficies, no puede predecir la decisión de un sitio de terceros y no sustituye probar las páginas reales que visita tu flujo.
Resumen
- Compara tres sesiones y no dos: con interfaz, headless y con perfil en el modo que despliegas.
- Usa la misma versión del fixture y registra los mismos campos en cada una.
- Compara los registros campo a campo y clasifica cada diferencia como esperada o sin explicar.
- Una diferencia sin explicar es un hallazgo que explicar, no un defecto que ocultar.
Contenido
- Empezar por un fixture propio
- Registrar los mismos campos tres veces
- Clasificar cada diferencia
- Mantener la comparación ejecutable
- Mantener la comparación honesta
- Comparar el despliegue, no solo los modos
- Conclusión práctica
Empezar por un fixture propio
La comparación solo tiene sentido en una página que controlas, con una versión fijada, que informe de los mismos campos en todos los modos. Una página de terceros que cambia sus propios scripts puede producir una diferencia que nada tiene que ver con el modo headless, y desde fuera no puedes saber de cuál de las causas se trata.
Escribe el fixture para que informe de valores y no de veredictos. Una página que imprime «headless detectado» es una página cuya lógica tendrías que auditar antes de fiarte. Una página que informa de navigator.userAgent, navigator.platform, screen.width, screen.height, devicePixelRatio, navigator.hardwareConcurrency, Intl.DateTimeFormat().resolvedOptions().timeZone y de la presencia de algunas APIs de construcción te da valores que puedes comparar sin creer ninguno.
Guarda la versión del fixture en el registro. Un fixture que cambia entre dos ejecuciones convierte una diferencia real en una diferencia del fixture, y la comparación culpará al navegador. Sube la versión cuando cambien los campos informados y repite la línea base al hacerlo.
Incluye al menos un campo que esperes que difiera. Una comparación en la que todo coincide puede significar que los modos coinciden, o que el fixture dejó de informar de algo. Una diferencia esperada actúa como control: si desaparece, el fixture cambió.
Registrar los mismos campos tres veces
La primera sesión es con interfaz, sin perfil. Es la referencia que un lector reconocerá: una ventana de navegador normal en una pantalla normal. La segunda es headless, también sin perfil, de modo que la única entrada que cambia es el modo. La tercera es la configuración de despliegue: el perfil que realmente publicas, en el modo en que realmente ejecutas.
Todas las sesiones usan la misma versión del paquete del navegador, la misma versión del fixture y la misma secuencia de peticiones. Cambiar más de una entrada entre sesiones vuelve ilegible la comparación, que es justo el error que esta comparación pretende evitar.
Registra los campos como un objeto plano para que una herramienta de diferencias los compare sin conocer la estructura. Dentro de un campo pueden ir objetos anidados, pero el nivel superior debe ser una lista de nombres y valores que una diferencia de líneas simple pueda mostrar. Un registro con veinte campos planos es más fácil de comparar que uno con cuatro anidados.
Guarda la salida de cada sesión por separado y con el nombre del modo. headed.json, headless.json y profile.json hacen evidente la comparación a tres bandas y permiten repetir una sesión sin repetir las tres.
Clasificar cada diferencia
Una diferencia entre con interfaz y headless no es automáticamente un problema. Algunas son esperadas porque el modo cambia lo que hace el navegador, y otras porque tu perfil de despliegue declara un valor que ninguna de las dos sesiones por defecto tenía.
El agente de usuario (navigator.userAgent) es la diferencia esperada más clara. Un navegador headless suele informar de una marca headless, y un perfil puede declarar otra. Eso es una declaración, no una fuga, y el registro debe indicar qué sesión la declaró.
Las dimensiones de pantalla (screen.width y screen.height) pueden diferir porque la pantalla virtual de una sesión headless no es la pantalla física de la sesión con interfaz. Un perfil que declara un tamaño de pantalla lo cambia otra vez. La pregunta interesante no es si difieren, sino si el tamaño declarado por el despliegue es coherente con los demás valores que dependen de él, como el tamaño de ventana y el viewport.
La relación de píxeles (devicePixelRatio) suele diferir por la misma razón. Una sesión headless con pantalla virtual por defecto puede informar de una relación mientras la sesión con interfaz informa de la de la pantalla física, y un perfil puede declarar una tercera. Registra la relación y el viewport que resulta de ella juntos, porque una relación sin viewport no describe lo que midió una página.
Las superficies de temporización y renderizado son las que más probablemente difieran por razones reales. Una sesión headless puede no tener GPU, componer de otra forma y producir una salida de canvas distinta. Esas diferencias son las que merece la pena investigar, porque son las que una página podría usar para distinguir los modos aunque todos los valores declarados coincidan.
Una diferencia que aparece solo en la sesión con perfil suele ser una declaración que surte efecto. Compárala con el archivo de perfil y no con la sesión con interfaz, y registra si el valor declarado y el observado coinciden. Esa comprobación es el motivo de ejecutar la tercera sesión.
Una diferencia que aparece solo en una ejecución de un modo no es esperada ni es una diferencia de modo. Es variación entre ejecuciones, y compararla con las otras dos sesiones inducirá a error. Ejecuta la misma sesión dos veces antes de clasificarla y trata como ruido una diferencia que solo aparece una vez hasta que se repita.
Mantener la comparación ejecutable
Una comparación que vive en un script de un solo uso es una comparación que nadie repite. Pon las tres sesiones detrás de un único comando, por ejemplo npm run compare:modes, escribe los tres registros en un directorio fijo y haz que la diferencia sea un comando y no un juicio. Cuando una actualización del navegador cambia algo, la comparación a tres bandas lo muestra en una línea por campo.
Mantén el fixture y los registros en el repositorio, con la versión incluida en el nombre del archivo. Un lector posterior puede ver qué campos se compararon en qué momento, y el historial de un campo que no deja de cambiar es visible sin preguntar a nadie.
Repite la comparación tras una actualización del navegador, un cambio de formato de perfil y una actualización del driver. Son los tres sucesos que más probablemente cambian un campo sin cambiar tu código, y la comparación es lo bastante barata como para ejecutarla en cada uno.
No conviertas la comparación en una puerta de paso o fallo para que los modos coincidan. Los modos pueden diferir; lo que la puerta debe detectar es un campo que dejó de registrarse en silencio o un valor declarado que dejó de aparecer. Una puerta que falla cada vez que los modos con interfaz y headless discrepan se ignorará en un mes.
Mantener la comparación honesta
Decidir qué conclusiones permite
Una comparación a tres bandas responde a una pregunta estrecha: para estos campos, en este fixture, en este paquete de navegador, qué difiere entre modos y qué difiere tras aplicar un perfil. No responde si un sitio aceptará una sesión headless, si una sesión headless es detectable en general ni si una sesión con interfaz es más segura. Esas preguntas dependen de la lógica del sitio, de su política de cuentas y de sus reglas de riesgo, nada de lo cual puede observar el fixture.
Escribe la conclusión en esa forma estrecha, citando el paquete (package@155) y el fixture usado. «En fixture@v4 con el paquete 155, el agente de usuario y el tamaño de pantalla difieren entre con interfaz y headless antes de aplicar un perfil, y coinciden con los valores declarados del perfil después» es una conclusión que el registro respalda. «Headless está bien» no lo es, e invita a una decisión que los datos no pueden sostener.
Mantén la comparación independiente de cualquier sitio de destino. En cuanto una sesión visita también una página de terceros, el registro mezcla el comportamiento de tu navegador con la respuesta de esa página, y la diferencia puede venir de cualquiera. Si necesitas comprobar una página real, hazlo en una ejecución aparte con su propio registro y deja limpia la comparación del fixture.
Comparte la diferencia, no la conclusión. Un colega que revisa un campo cambiado puede decidir qué significa para su flujo; un colega que recibe un veredicto tiene que aceptarlo por confianza. Los tres registros y la diferencia de una línea son el artefacto que merece la pena conservar.
Actualiza la conclusión cuando cambie el paquete o el formato de perfil. Una conclusión escrita para una versión del paquete describe esa versión, y un lector posterior que la encuentre asociada a otra la aplicará mal. Pon fecha a la conclusión y nombra las entradas a las que se aplica.
Tratar los campos que es fácil confundir
Algunos campos parecen señales del navegador pero son valores de petición o de entorno, y compararlos entre modos sin saberlo desperdicia la comparación. Las cabeceras HTTP que envía una sesión se construyen a partir de varias fuentes, y la cabecera de agente de usuario puede diferir de navigator.userAgent de maneras que dependen del driver y de las opciones de arranque.
Registra a la vez el valor visible en la página y la cabecera de la petición cuando ambos deben coincidir. Una discrepancia entre ellos es un hallazgo real, y es invisible si el fixture solo informa del valor de la página. Captura la cabecera de la petición en el mismo momento que el valor de la página para que ambos describan la misma navegación.
La configuración regional y la zona horaria merecen el mismo trato. Intl.DateTimeFormat().resolvedOptions().locale, Intl.DateTimeFormat().resolvedOptions().timeZone y navigator.language son valores separados que pueden discrepar, y una sesión headless en un servidor con otra configuración regional del sistema puede producir una combinación que la sesión con interfaz nunca muestra. Registra los tres y no uno solo.
Las medidas de canvas y de fuentes requieren cuidado porque dependen del renderizado, que es precisamente donde una sesión headless puede diferir legítimamente. Registra la medida y la ruta de renderizado juntas, y trata una diferencia como esperada cuando la ruta difiere y como digna de investigación cuando no. La guía de rutas de renderizado de canvas explica por qué dos rutas por software pueden producir salidas distintas en el mismo equipo.
Hacer legible la diferencia
La diferencia entre dos registros JSON es legible cuando los registros comparten una forma plana y los valores son cortos. Da formato a los registros con claves ordenadas, un campo por línea y sin anidación más allá de un nivel. Basta una herramienta que imprima <field>: <headed> -> <headless> para cada campo cambiado; no hace falta un formato de informe propio.
Incluye en cada registro un campo con el modo de la sesión y otro con el perfil, para que un registro copiado fuera de contexto siga diciendo qué lo produjo. Un registro sin esos dos campos no se puede comparar después, y la comparación depende de saber qué sesión es cuál.
Mantén un pequeño historial de campos junto al fixture. Cuando se añade un campo, anota la fecha y el motivo; cuando se quita, anótalo también, porque un campo quitado se ve igual que uno que dejó de informar. El historial es lo que distingue un cambio intencionado de uno silencioso.
Ejecuta la diferencia en integración continua y haz que falle solo cuando un campo desaparezca o un valor declarado deje de coincidir con el perfil. Una diferencia que falla en cada discrepancia de modo se desactivará, y una comparación desactivada es peor que una que informa y deja que una persona lea.
Revisar antes de una publicación
Una revisión de publicación pregunta si el despliegue sigue informando de lo que declaró. La comparación a tres bandas lo responde directamente: los valores de la sesión con perfil frente al archivo de perfil, y las dos sesiones por defecto frente a su propia línea base. Ejecútala como parte de la revisión de publicación y no después de un incidente, y guarda el resultado junto a profile.json.
Conserva la comparación anterior durante el periodo soportado. La diferencia entre los registros de esta publicación y los de la anterior es más informativa que cualquiera de los dos por separado, y es la única forma de ver un campo que cambió poco a poco a lo largo de dos actualizaciones.
Retira una comparación cuando la versión del fixture que usó ya no esté en servicio, y conserva la nota que dice qué publicación describía. Una comparación obsoleta que parece vigente es el tipo de documento más caro, porque se le cree.
Comparar el despliegue, no solo los modos
La comparación a tres bandas dice qué difiere entre modos. No dice si el despliegue es coherente consigo mismo, que es una pregunta distinta con un registro distinto. Una sesión con perfil puede coincidir con su agente de usuario declarado y aun así declarar un tamaño de pantalla que no encaja con el de su ventana, y ni la diferencia ni una comprobación de un solo campo lo detectan.
Añade una pasada de coherencia que compare los campos de la sesión con perfil entre sí y no con las demás sesiones. El viewport (window.innerWidth y window.innerHeight) debe caber dentro de la pantalla declarada; la zona horaria, la configuración regional y el idioma deben describir la misma región; la plataforma debe ser compatible con el agente de usuario. Cada una es una regla que puedes enunciar en una línea y comprobar en la misma ejecución del fixture.
Guarda las reglas junto al fixture y ponle nombre a cada una. Cuando una regla falla, el registro dice qué regla, no solo qué campo cambió, y el lector no tiene que redescubrir la relación. Las reglas también sobreviven al cambio de nombre de un campo, algo que una comparación fija no logra.
Ejecuta la pasada de coherencia en todos los tipos de sesión, no solo en la del perfil. Una sesión con interfaz y sin perfil puede violar una regla si la configuración regional y la zona horaria del equipo discrepan, y saber que la propia línea base es incoherente cambia cómo lees todas las demás diferencias.
Informa de los fallos de reglas por separado de las diferencias de modo. Una diferencia de modo es esperada e informativa; un fallo de regla es un problema de configuración del que se encarga el despliegue. Mezclarlos en una sola lista vuelve ruidosas las diferencias esperadas y fáciles de pasar por alto los problemas de configuración.
Usar la comparación cuando llega un informe
Un informe de que un sitio «detecta la automatización» llega como una frase, no como un registro. La comparación es lo que convierte la frase en una pregunta: qué campo cambió desde la última comparación buena conocida, y si cambió en el modo que usa el flujo.
Pide primero el identificador de ejecución (runId) y la comparación del mismo despliegue. Sin ellos, la primera hora se va en establecer qué se estaba ejecutando, que es la información que la comparación ya guarda. Con ellos, quien revisa puede ir directo al campo cambiado.
Compara con el último registro bueno de la misma versión del fixture. Una diferencia con una versión anterior del fixture mezcla una diferencia del navegador con una del fixture, y la lista resultante es más larga de lo necesario. Cuando no exista registro para el fixture actual, genéralo antes de investigar.
Trata un campo ausente como un hallazgo en sí mismo. Un campo que se registró la semana pasada y falta esta semana puede significar que el fixture dejó de informar de él, que la página falló antes de llegar a él o que el valor dejó de estar disponible. Cada caso tiene un siguiente paso distinto, y ninguno es «el sitio empezó a detectarnos».
Haz la comparación lo bastante barata para ejecutarla bajo demanda. Una comparación que requiere una tarde de preparación se ejecutará después del incidente y no durante él, y el incidente habrá terminado antes de que existan los registros.
Conclusión práctica
Los navegadores headless y con interfaz difieren de maneras esperadas, de maneras declaradas por un perfil y de maneras que merece la pena investigar. Ejecuta tres sesiones sobre un fixture propio, registra los mismos campos planos, clasifica cada diferencia y mantén la comparación ejecutable. Úsala junto a la guía de recopilación de datos y la guía de configuración de servidor headless.
Fuentes
Este artículo forma parte de la guía: Despliegue y versión del navegador: mapa operativo
Artículos Relacionados
Consistencia de perfiles entre navegadores headless y headed
DespliegueSeñales de automatización y coherencia en Playwright
DespliegueProtección de huellas del navegador para recopilar datos web
Huella digitalFingerprinting de permisos: privacidad y coherencia
DespliegueRegistros de despliegue del navegador y minimización de privacidad
DesplieguePlanificación de capacidad de BrowserContext
Lleva BotBrowser de la investigación a producción
Usa estas guías para entender el modelo y después avanzar hacia validación multiplataforma, contextos aislados y despliegue de navegador preparado para escalar.