Tráfico de bots vs lectores reales: cómo medirlo (mi caso)
Lo esencial
- Medí a mano los logs de mi servidor: 158.609 peticiones a artículos en 27 días y el 94,2 % eran bots. Googlebot solo, 136.267.
- El dato que presumía (13,6 visitas por artículo) salía de ahí: líneas de log, no personas. Contra GA4, la diferencia era de 31 a 1.
- Un filtro por user-agent sirve para depurar, no para contar lectores. Para eso solo valen Search Console y Analytics, y mi agente no tiene la culpa de que yo preguntara mal.
Ayer me enteré de que llevaba tres meses celebrando un número que no era de nadie. 13,5 visitas por artículo. Estaba en mi panel, lo calculaba un script mío, y me lo creí como se cree una analítica: sin mirar debajo.
Lo peor no es el número. Lo peor es que durante todo ese tiempo me sirvió para una cosa muy concreta: decidir que el proyecto iba bien. Cuando el dato que te tranquiliza es falso, no tienes un problema de datos, tienes un problema de criterio. Y el mío lo escribí yo, en una línea de código, sin darme cuenta.
Tres meses celebrando un número que no era nadie
El origen era sencillo y hasta razonable: un script que leía los logs de Caddy y contaba peticiones a las páginas de artículos. Cada línea del log es una petición HTTP que el servidor atendió. Sumabas, dividías entre artículos y salía una cifra bonita.
Lo que yo entendía al leer esa cifra era "gente leyendo". Lo que la cifra decía de verdad era "el servidor respondió". Son dos frases separadas por un abismo, pero viven en el mismo número, así que nadie discute.
La culpa de creérmelo es mía y tiene una explicación poco noble: era el único número que crecía. Cuando tienes 450 artículos, 110 automatizaciones y cero ingresos, un dashboard con una columna alta compensa. No lo puse ahí por error. Lo dejé ahí porque me gustaba.
Esta tarde abrí los logs a mano
Hoy he hecho lo que debí hacer en agosto: leer los logs yo mismo y separar peticiones por user-agent, sin intermediarios. La ventana que tengo guardada va del 19 de agosto a hoy, 27 días. Los resultados, sin adornos:
| Medición (19-ago 18:30 → 14-sep 19:10) | Valor |
|---|---|
| Peticiones HTML a artículos | 158.609 |
| De ellas, bots | 149.360 (94,2 %) |
| De ellas, Googlebot | 136.267 |
| Peticiones con user-agent que no es bot | 9.249 (343 al día) |
| Rutas de artículo distintas pedidas | 679 |
| Media por ruta: el número de mi panel | 13,6 |
Dividí las peticiones "no bot" entre las rutas pedidas: 9.249 entre 679, un 13,6. Ahí estaba mi famoso 13,5, y ya sé de dónde salía: de Googlebot releyendo un sitio que Google había dejado de mostrar. Ocho de cada diez peticiones a mis artículos las hacía el buscador, no una persona.
Y todavía quedaba la parte incómoda. Esas 9.249 peticiones "no bot" son 343 al día. Ese número tampoco son lectores: ahí dentro está mi propio navegador, mi terminal, lectores de RSS y algún scraper que se pone un user-agent de Chrome porque nadie se lo impide. La prueba está al otro lado: GA4, que solo cuenta páginas que ejecutan JavaScript, me da 309 visitas de artículos en 28 días. Once al día.
Trescientas cuarenta y tres contra once. Ese factor de treinta y uno es toda la diferencia entre "mi web la lee gente" y "mi web recibe peticiones". Y lo descubrí ejecutando esto:
zcat -f /var/log/caddy/access-*.log.gz /var/log/caddy/access.log \
| python3 -c 'import sys,json,re
corte=1789405800; bot=re.compile(r"bot|spider|crawl",re.I); t=b=h=0
for l in sys.stdin:
try: r=json.loads(l)
except: continue
if r.get("ts",0)>corte: continue # ventana cerrada 14-sep 19:10
u=r.get("request",{})
if u.get("host")!="lafronteraia.com" or u.get("method")!="GET": continue
if not u["uri"].startswith("/articulos/") or not u["uri"].endswith(".html"): continue
ua=u.get("headers",{}).get("User-Agent",[""])[0]
t+=1
if bot.search(ua): b+=1
else: h+=1
print(t,b,h)'
Con eso salió 158609 149360 9249, que es exactamente la tabla de arriba. No hay nada exótico en ese comando. Lo que no había era yo mirándolo con la pregunta correcta delante.
Lo que dice Google, que es el único número que puedo defender
Si los logs no cuentan lectores, ¿qué los cuenta? Los que se pueden auditar delante de cualquiera: Search Console para lo que llega desde búsqueda, Analytics para sesiones con navegador de verdad. Estos son mis datos de hoy, con sus ventanas tal cual:
- Search Console, 90 días: 62 clics sumando todas las secciones del sitio.
- Artículos con alguna impresión en esos 90 días: 148 de 450. Los otros 302 no aparecieron ni una vez.
- Impresiones en los últimos 14 días: 0, frente a 34 en los 14 anteriores. Eso es el colapso del 22 de agosto, contado en frío.
- GA4, 28 días: 309 visitas a páginas de artículo.
El sitio no está mal montado: Googlebot entra, las páginas responden 200 y 265 de 450 URLs están indexadas. Lo que pasa es que un servidor sano puede servir a nadie, y yo estaba midiendo la salud del servidor y llamándola audiencia.
Mi agente no mintió: yo pregunté mal
Aquí viene la parte que me interesa contar, porque es la que tiene moraleja para cualquiera que ponga una IA a medir cosas.
El script no inventó nada. Contó peticiones porque le pedí peticiones. Devolvió exactamente la definición que yo escribí, no la que yo tenía en la cabeza. Un agente obediente es peligrosísimo en analítica precisamente por eso: te da la métrica que pediste, la ejecuta millones de veces sin cansarse y la pone en una tabla con dos decimales, que es lo que la hace parecer verdad.
Descubrí que llevo meses protegiéndome del fallo equivocado. Tengo guardas, reintentos y alertas para que mis robots no mueran en silencio. Nadie, ni yo, tenía una guarda para el caso contrario: un robot funcionando perfectamente y midiendo lo que no era.
Personalmente, lo que más me ha costado aceptar es la parte de vanidad. Durante tres meses tuve una excusa para no mirar Google. La tenía en el panel, subiendo.
Qué cambié, y por qué en código
La primera reacción fue escribir una regla en el prompt de mis publicadores: "los titulares deben empezar por lo que la gente busca". Mal camino. Ya me había pasado antes: el 10 de agosto cambié la estrategia editorial en el documento y el prompt del cron nunca lo mencionó, así que pasé un mes publicando igual que antes sin que nadie se enterara.
Las reglas que viven en un prompt se evaporan en la siguiente edición. Así que esto vive en un guard que corre solo: title-format-guard.py, que vigila dos cosas distintas. Una, que los prompts de los tres publicadores sigan llevando su bloque de formato; si alguien lo borra, salta. Y dos, el efecto real: qué porcentaje de lo publicado en 14 días usa un titular que la gente busca de verdad, con la lista de los que empiezan hablando de mí.
Lo demás fue ordenar las fuentes según para qué sirven. Los logs de Caddy quedan para depurar el servidor, que es lo que hacen bien. Los lectores se miden con Search Console y con la API de GA4. Y una regla que me puse a mí mismo: no vuelvo a presumir de un número que no pueda reproducir delante de otra persona en treinta segundos.
Si estás en la misma situación, el test es este: abre tus logs, filtra por user-agent y divide. Si el resultado no cuadra con Analytics, el que miente es el log. Siempre.
Conclusión
Un log es un latido; un clic es una señal. Confundirlos es fácil porque los dos llegan en forma de número y crecen cuando tú quieres que crezcan.
Lo que me queda de este episodio no es una métrica nueva, es un hábito: cuando un dato me alegra, lo trato como sospechoso hasta que lo puedo explicar de dónde sale. Tardé tres meses en aplicar a mi propia web lo que aplico a mis robots desde el primer día: si no puedo provocar el fallo, no sé si la guarda funciona. Pues igual con los buenos datos: si no puedo explicar quién los genera, no sé si existen.