Grabación de sesiones con el script de Flowsery
La grabación de sesiones te permite reproducir lo que hicieron los visitantes reales en tu sitio, movimientos del ratón, clics, datos introducidos en formularios, cambios de página, con solo añadir un pequeño script a tu HTML. El grabador es un bundle JS independiente del tracker principal de analítica, así que solo se carga cuando lo activas.
El grabador está basado en rrweb y envía sus eventos de mutación del DOM a Flowsery en fragmentos comprimidos con gzip. El primer fragmento es una instantánea completa; los siguientes son incrementales.
Requisitos previos
- La grabación debe estar activada para el sitio web en tu panel de Flowsery (
Ajustes del sitio → General → Grabación de sesiones). El servidor rechaza las solicitudesrecording_startcuando está desactivada. - Ya tienes en marcha el tracker principal de analítica (
/js/main.js). La grabación usa el mismodata-fl-website-id, las mismas cookies de visitante y de sesión y el mismo endpoint/eventsque el tracker principal.
Inicio rápido (CDN)
Añade la etiqueta script justo después de tu tracker principal de analítica:
<script defer data-fl-website-id="flid_******" src="https://cdn.flowsery.com/main.js"></script>
<script defer data-fl-website-id="flid_******" src="https://cdn.flowsery.com/recording.js"></script>El grabador aplica los mismos filtros que el tracker principal (detección de bots, localhost, iframe, file://, autoexclusión) y se detiene sin errores cuando se cumple alguno.
¿Prefieres una sola etiqueta? Añade el atributo data-recording a tu tracker principal y Flowsery inyecta el grabador por ti, sin necesidad de un segundo <script>:
<script defer data-fl-website-id="flid_******" data-recording src="https://cdn.flowsery.com/main.js"></script>Activar con el SDK de npm
Si inicializas Flowsery con el SDK de npm, activa la grabación con una sola opción en lugar de una etiqueta script:
import { initFlowsery } from 'flowsery';
await initFlowsery({
websiteId: 'flid_******',
recording: true,
});El SDK carga de forma diferida el bundle del grabador desde la CDN de Flowsery en la primera inicialización, así que rrweb nunca entra en el bundle de tu aplicación. Los eventos de seguimiento siguen pasando por tu apiBase cuando está definido. Para grabar solo una parte de las sesiones, añade recordingSampleRate, y para analizar con IA solo una parte de ellas, añade aiSampleRate (ambos 0–100, valor predeterminado 100): consulta Muestreo. Todo lo que sigue, el requisito del panel, los modos de privacidad, los filtros y la compatibilidad con navegadores, se aplica igual.
Configuración con proxy inverso (recomendada para producción)
Los bloqueadores de anuncios y las extensiones de protección contra el rastreo bloquean los scripts de terceros y analytics.flowsery.com directamente. Si sirves tanto el script como el endpoint de eventos desde tu propio dominio, lo evitas, y las IP de los visitantes y la precisión se mantienen intactas.
El grabador deduce automáticamente la base de la API a partir de su propio <script src>, igual que el tracker principal. Mientras el bundle de grabación se sirva en /js/recording.js de tu dominio, no hace falta configurar nada más.
Next.js
// next.config.js
async rewrites() {
return [
{ source: '/js/main.js', destination: 'https://cdn.flowsery.com/main.js' },
{ source: '/js/recording.js', destination: 'https://cdn.flowsery.com/recording.js' },
{ source: '/api/track', destination: 'https://analytics.flowsery.com/analytics/events' },
];
}<script defer data-fl-website-id="flid_******" src="/js/main.js"></script>
<script defer data-fl-website-id="flid_******" src="/js/recording.js"></script>Otros proxies
El mismo esquema sirve para cualquier proxy inverso. Asigna las tres rutas:
| Ruta pública | Origen |
|---|---|
/js/main.js | https://cdn.flowsery.com/main.js |
/js/recording.js | https://cdn.flowsery.com/recording.js |
/api/track | https://analytics.flowsery.com/analytics/events |
Para guías de proxy específicas de cada framework (Astro, Caddy, Express, FastAPI, Nginx, PHP, Vue, etc.), consulta las guías de proxy del centro de ayuda.
Configuración
El grabador lee su configuración de los mismos atributos de la etiqueta <script> que el tracker principal. La mayoría de las opciones se heredan automáticamente; las siguientes son propias de la grabación.
data-rrweb-src (opcional)
Sustituye la URL que usa el grabador para cargar rrweb. El valor predeterminado es https://cdn.jsdelivr.net/npm/rrweb@2/dist/rrweb.min.js.
<script defer data-fl-website-id="flid_******" data-rrweb-src="/js/rrweb.min.js" src="/js/recording.js"></script>Es útil cuando tu CSP prohíbe cdn.jsdelivr.net o cuando quieres alojar rrweb tú mismo para evitar solicitudes a terceros.
data-recording-sample (opcional)
Graba solo un porcentaje de las sesiones. Acepta un número entero 0–100.
- Predeterminado:
100: graba todas las sesiones. Omite el atributo para grabar por completo. - Ejemplo:
data-recording-sample="30"graba ~30% de las sesiones.
<script defer data-fl-website-id="flid_******" data-recording-sample="30" src="https://cdn.flowsery.com/recording.js"></script>La decisión es determinista por sesión: un visitante se graba u omite de forma consistente durante toda su sesión, a través de recargas y navegaciones entre páginas, de modo que nunca obtienes visitas grabadas a medias. Las sesiones no muestreadas se saltan por completo la descarga del grabador. Consulta Muestreo para saber cómo se combina esto con el muestreo del análisis con IA.
data-ai-sample (opcional)
De las sesiones que grabas, envía solo un porcentaje al análisis de sesiones con IA. Acepta un número entero 0–100.
- Predeterminado:
100: analiza todas las sesiones grabadas. - Ejemplo:
data-ai-sample="50"envía ~50% de las sesiones grabadas al análisis con IA.
<script defer data-fl-website-id="flid_******" data-recording-sample="30" data-ai-sample="50" src="https://cdn.flowsery.com/recording.js"></script>El grabador envía esta tasa a Flowsery en el handshake de recording_start; el servidor la almacena en la grabación y la aplica cuando se encola el análisis (para entonces el navegador ya se ha ido hace tiempo). Consulta Muestreo.
Niveles de privacidad
El modo de privacidad se decide en el servidor y se envía al grabador en la respuesta de recording_start. Ahora mismo todos los sitios web reciben balanced, y el panel no tiene ningún ajuste para cambiarlo. Modos disponibles:
- balanced (predeterminado), Enmascara los campos de contraseña, email y teléfono. Los demás campos y el texto se graban tal cual.
- strict: Enmascara todos los campos y todo el contenido de texto, bloquea
<video>,<audio>y<canvas>y desactiva la grabación de canvas. - relaxed: Sin enmascaramiento. Úsalo solo en aplicaciones internas en las que controlas los datos introducidos.
Para tener otro modo en una página, tendrías que posprocesar las grabaciones en el servidor; el script respeta lo que devuelva el servidor.
Muestreo
En sitios con mucho tráfico rara vez necesitas grabar cada visita. Flowsery aplica muestreo en dos etapas independientes, ambas configuradas en el script (o el SDK de npm): no hay nada que configurar en el panel:
- Tasa de muestreo de grabación: la proporción de sesiones que llegan a grabarse. Se define con
data-recording-sample(o la opciónrecordingSampleRatedel SDK),0–100, valor predeterminado100. El navegador decide, por lo que las sesiones no muestreadas nunca descargan el grabador. - Tasa de muestreo de análisis con IA: la proporción de sesiones ya grabadas que se envían al análisis de sesiones con IA. Se define con
data-ai-sample(o la opciónaiSampleRatedel SDK),0–100, valor predeterminado100. El grabador la envía a Flowsery en el handshake derecording_starty el servidor la aplica por grabación, ya que el análisis con IA se ejecuta después de que la página se haya cerrado.
Las dos se multiplican. Con una tasa de grabación del 30% y una tasa de IA del 50%, se graba ~30% de las sesiones y ~15% (30% × 50%) obtiene un veredicto de IA: el resto sigue siendo totalmente reproducible, solo que sin análisis con IA.
El muestreo es determinista, no aleatorio: la decisión de grabación se basa en la sesión, de modo que un visitante se graba (o no) de forma consistente durante toda su sesión y a través de recargas. Los valores predeterminados de 100 mantienen el comportamiento original, grabar y analizar todo, así que nada cambia hasta que reduzcas una tasa.
Cómo funciona
- El script lee
data-fl-website-idde su propia etiqueta y obtiene los UID de visitante y de sesión de las cookies que define el tracker principal (_fs_vid,_fs_sid). - Envía
recording_startpor POST a/events. Si el servidor lo rechaza (grabación desactivada, cuota superada, etc.), el script se detiene. - Si se acepta, carga rrweb y empieza a capturar eventos en un búfer en memoria.
- Cada 15 segundos (o antes si el búfer supera los 500 eventos / 256 KB), el búfer se convierte a JSON Lines → se comprime con gzip → se codifica en base64 → se envía por POST como
recording_chunk. El servidor guarda los fragmentos en R2 y agrega métricas (número de eventos, indicadores de clic/entrada/error/rage click, duración) en la fila de la grabación. - En
pagehide/beforeunload, se envía un fragmento final confinalize: truea través de un transporte seguro ante la descarga de la página,navigator.sendBeaconpara configuraciones con proxy del mismo origen, o una peticiónfetchcon keepalive para configuraciones directas (de origen cruzado): de modo que el último fragmento sobreviva al desmontaje de la página. El servidor cambia el estado de la grabación areadypara que aparezca en la lista de grabaciones del panel.
Compatibilidad con navegadores
- Chrome/Edge 80+, Firefox 113+, Safari 16.4+: compatibilidad completa, compresión gzip mediante
CompressionStream. - Versiones antiguas de Safari / Firefox: el grabador recurre a fragmentos sin gzip. Se guardan, pero la reproducción en el panel requiere la codificación gzip, así que las grabaciones de navegadores antiguos no se podrán reproducir hasta que el decodificador las admita.
- Modo privado de Safari:
sessionStoragees por pestaña; se genera un UID de grabación nuevo cada vez que se abre una pestaña. Por lo demás, funciona. - Modo sin cookies (
data-cookielessen el tracker principal): la grabación está desactivada porque no hay UID de visitante ni de sesión al que asociarla.
Coste y rendimiento
- Red: una sesión típica ocupa entre 50 y 500 KB comprimida; mucho menos que un vídeo. Los fragmentos se envían cada 15 s por una conexión
keepaliveo mediante la Beacon API al descargar la página. - CPU: rrweb ejecuta un MutationObserver en reposo: medible, pero muy por debajo del 1% de CPU en páginas típicas. La descarga de trabajo mediante
requestIdleCallbackviene integrada en rrweb 2.x. - Privacidad: el grabador respeta
Do Not Tracksolo cuando se definedata-respect-dnt="true". Si no, graba a todos los visitantes que llegan arecording_start.
Desactivar la grabación
Tres opciones:
- Globalmente: desactiva la grabación en los ajustes del sitio web. El servidor rechazará
recording_starty el script se detendrá. - Por página: no incluyas la etiqueta
<script src="/js/recording.js">. - Por visitante: define
localStorage.setItem('flowsery_ignore', 'true'): la misma exclusión que usa el tracker principal.
Solución de problemas
La pestaña Grabaciones está vacía
- Comprueba que la grabación está activada en el sitio web.
- Abre DevTools → Network. Deberías ver un POST
recording_startcorrecto seguido de POSTrecording_chunkperiódicos. - Revisa la respuesta de
recording_start: si esaccepted: false, el script se detiene; la respuesta también te indica el motivo (cuota superada, grabación desactivada).
"Esta grabación es demasiado corta para reproducirla"
La grabación existe, pero tiene menos de 2 eventos. Causas habituales:
- El visitante cerró la pestaña antes de que se pudiera enviar el primer fragmento.
- Un bloqueador de anuncios impidió que rrweb se cargara. Busca errores de CSP o de red en la consola.
- Faltaba
CompressionStreamy la codificación alternativa guardó un fragmento que el reproductor del panel todavía no puede decodificar.
La grabación se carga, pero mi proxy inverso no entra en acción
Asegúrate de que el script se sirve exactamente desde /js/recording.js. La expresión regular de detección automática reconoce script.js, main.js y recording.js (con un sufijo .hash.js opcional); las demás rutas recurren al host de analítica fijo en el código. Si tienes que servirlo desde otra ruta, define data-api="https://yourdomain.com/api" de forma explícita.