Seguimiento de primera parte con Cloudflare Pages
Configura un proxy de primera parte usando Cloudflare Pages Functions. Esta es la mejor opción si ya estás alojando tu sitio en Cloudflare Pages.
Dificultad: Fácil - Solo agrega un archivo a tu proyecto. Toma aproximadamente 5 minutos.
Por qué funciona (Mismo origen)
Este enfoque utiliza /api/_z/script.js que está en el mismo origen que tu sitio web. Firefox ETP y otras protecciones de seguimiento solo bloquean solicitudes entre orígenes diferentes.
- El navegador ve:
tudominio.com/api/_z/script.js→ Mismo origen ✅ - Tu función hace proxy servidor a servidor hacia Zenovay (el navegador nunca ve esto)
- Toda protección de seguimiento se evita porque la solicitud es de primera parte
Antes de comenzar
Asegúrate de tener:
- Un proyecto desplegado en Cloudflare Pages
- Tu código de seguimiento de Zenovay (encontrado en Dominios, abre tu sitio, luego la pestaña General — en la tarjeta de script de seguimiento)
- Acceso al código fuente de tu proyecto
Formato de tu código de seguimiento
Tu código de seguimiento se ve así: ZV_XXXXXXXXXX
- Comienza con
ZV_ - Seguido de 10 caracteres (letras y números)
- SENSIBLE A MAYÚSCULAS - cópialo exactamente
- Ejemplo:
ZV_Q8U0GYD70WR

Cómo funciona
Cuando agregas una carpeta functions a tu proyecto de Cloudflare Pages, Cloudflare crea automáticamente funciones sin servidor. Crearemos una función que haga proxy de solicitudes a la API de Zenovay, haciéndolas aparecer como solicitudes de primera parte en los navegadores.
Navegador → tudominio.pages.dev/api/_z/script.js ✅ (Primera parte)
↓
Función de Cloudflare
↓
api.zenovay.com/fp/script.js (Servidor a servidor)
Paso 1: Crear el directorio de funciones
La ubicación del archivo es CRÍTICA
La carpeta functions DEBE estar en la raíz de tu proyecto. No dentro de src/, no dentro de public/.
Ubicación correcta:
tu-proyecto/
├── functions/ ← AQUÍ (raíz del proyecto)
│ └── api/
│ └── _z/
│ └── [[path]].ts
├── src/
├── public/
└── package.json
UBICACIONES INCORRECTAS:
- ❌
src/functions/api/_z/[[path]].ts- Las funciones no funcionarán - ❌
public/functions/api/_z/[[path]].ts- Las funciones no funcionarán
Crea la estructura de directorios:
# Crear la estructura del directorio de funciones
mkdir -p functions/api/_z
# Verifica que estés en el lugar correcto
ls -la
# Deberías ver: functions/ src/ public/ package.json etc.Después de ejecutar estos comandos, la estructura de tu proyecto debería verse así:
tu-proyecto/
├── functions/
│ └── api/
│ └── _z/
│ └── [[path]].ts ← Lo crearemos a continuación
├── src/
├── public/
├── package.json
└── ...
Paso 2: Crear la función proxy
El nombre del archivo es IMPORTANTE
El archivo DEBE llamarse [[path]].ts (o [[path]].js para JavaScript):
- Dos corchetes de apertura:
[[ - La palabra
path - Dos corchetes de cierre:
]] - Extensión de archivo:
.tso.js
Esto crea una ruta "comodín" que maneja cualquier ruta después de /api/_z/.
¿TypeScript o JavaScript?
- Usa
.tssi tu proyecto usa TypeScript - Usa
.jssi tu proyecto usa JavaScript - Ambos funcionan exactamente de la misma manera
Si no estás seguro, intenta .ts primero. Si obtienes errores de TypeScript durante la compilación, renómbralo a .js en su lugar.
Crea el archivo functions/api/_z/[[path]].ts con este contenido:
/**
* Zenovay First-Party Proxy
* This function proxies tracking requests to make them first-party
*/
interface EventContext {
request: Request
params: { path?: string[] }
}
export async function onRequest(context: EventContext): Promise<Response> {
const { request, params } = context
// Build path from catch-all parameter
// [[path]] captures: script.js, e/CODE, settings/CODE, etc.
const path = (params.path || []).join('/')
const url = new URL(request.url)
const targetUrl = `https://api.zenovay.com/fp/${path}${url.search}`
// Handle CORS preflight requests
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
})
}
try {
// Get the real client IP for accurate geolocation
const clientIP = request.headers.get('CF-Connecting-IP') || ''
// Forward request with real IP
const proxyHeaders = new Headers(request.headers)
proxyHeaders.set('X-Zenovay-Real-IP', clientIP)
proxyHeaders.delete('Host')
// Build the request
const requestInit: RequestInit = {
method: request.method,
headers: proxyHeaders,
}
// Include body for POST/PUT/PATCH requests
if (['POST', 'PUT', 'PATCH'].includes(request.method)) {
requestInit.body = await request.arrayBuffer()
}
// Forward to Zenovay
const response = await fetch(targetUrl, requestInit)
// Add CORS headers to response
const responseHeaders = new Headers(response.headers)
responseHeaders.set('Access-Control-Allow-Origin', '*')
return new Response(response.body, {
status: response.status,
headers: responseHeaders,
})
} catch (error) {
console.error('Proxy error:', error)
return new Response(JSON.stringify({ error: 'Proxy error' }), {
status: 502,
headers: { 'Content-Type': 'application/json' },
})
}
}Alternativa en JavaScript
Si prefieres JavaScript (sin TypeScript), crea functions/api/_z/[[path]].js:
/**
* Zenovay First-Party Proxy
* This function proxies tracking requests to make them first-party
*/
export async function onRequest(context) {
const { request, params } = context
// Build path from catch-all parameter
const path = (params.path || []).join('/')
const url = new URL(request.url)
const targetUrl = `https://api.zenovay.com/fp/${path}${url.search}`
// Handle CORS preflight requests
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
},
})
}
try {
// Get the real client IP for accurate geolocation
const clientIP = request.headers.get('CF-Connecting-IP') || ''
// Forward request with real IP
const headers = new Headers(request.headers)
headers.set('X-Zenovay-Real-IP', clientIP)
headers.delete('Host')
// Forward to Zenovay
const response = await fetch(targetUrl, {
method: request.method,
headers: headers,
body: ['POST', 'PUT', 'PATCH'].includes(request.method)
? await request.arrayBuffer()
: undefined,
})
// Add CORS headers to response
const responseHeaders = new Headers(response.headers)
responseHeaders.set('Access-Control-Allow-Origin', '*')
return new Response(response.body, {
status: response.status,
headers: responseHeaders,
})
} catch (error) {
return new Response(JSON.stringify({ error: 'Proxy error' }), {
status: 502,
headers: { 'Content-Type': 'application/json' },
})
}
}Paso 3: Agregar el script de seguimiento
Agrega este script al HTML de tu sitio web. La ubicación depende de tu marco de trabajo:
<!-- Zenovay Analytics - First-Party Tracking -->
<script defer
data-tracking-code="YOUR_TRACKING_CODE"
src="/api/_z/script.js">
</script>Reemplaza YOUR_TRACKING_CODE con tu código de seguimiento real del panel de Zenovay (por ejemplo, ZV_Q8U0GYD70WR).
Ejemplos específicos del marco de trabajo
React / Next.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<script
defer
data-tracking-code="YOUR_TRACKING_CODE"
src="/api/_z/script.js"
/>
</head>
<body>{children}</body>
</html>
)
}Vue / Nuxt
<!-- In app.vue -->
<script setup>
useHead({
script: [
{
src: '/api/_z/script.js',
defer: true,
'data-tracking-code': 'YOUR_TRACKING_CODE'
}
]
})
</script>Astro
---
const { title } = Astro.props;
---
<html lang="en">
<head>
<title>{title}</title>
<script defer data-tracking-code="YOUR_TRACKING_CODE" src="/api/_z/script.js"></script>
</head>
<body>
<slot />
</body>
</html>HTML plano
<!DOCTYPE html>
<html>
<head>
<title>My Website</title>
<script defer data-tracking-code="YOUR_TRACKING_CODE" src="/api/_z/script.js"></script>
</head>
<body>
<!-- Your content -->
</body>
</html>Paso 4: Desplegar
Despliega tu proyecto a Cloudflare Pages:
Opción A: Usando Git (Recomendado)
Si tienes despliegues automáticos desde Git:
git add .
git commit -m "Add Zenovay first-party tracking"
git pushCloudflare Pages construirá e implementará automáticamente.
Opción B: Usando Wrangler CLI
# Construir tu proyecto primero
npm run build
# Desplegar a Cloudflare Pages
npx wrangler pages deploy ./dist --project-name=your-project-nameReemplaza ./dist con tu directorio de salida de compilación (podría ser ./out, ./build, etc.) y your-project-name con el nombre de tu proyecto de Cloudflare Pages.
Paso 5: Verificar que funciona
Verificación 1: Pestaña de funciones en Cloudflare
- Ve a Cloudflare Dashboard → Pages
- Haz clic en tu proyecto
- Haz clic en la pestaña Functions
- Deberías ver
api/_z/[[path]]en la lista
¿No ves la función?
- Asegúrate de que la carpeta
functions/está en la raíz del proyecto (no dentro desrc/) - Asegúrate de que el archivo se nombra
[[path]].tso[[path]].js - Verifica que el despliegue se completó exitosamente
Verificación 2: Pestaña de red en el navegador
- Abre tu sitio desplegado
- Presiona F12 (o Cmd+Option+I en Mac) para abrir DevTools
- Haz clic en la pestaña Network
- Recarga la página (Cmd+R o Ctrl+R)
- En el cuadro de filtro, escribe
script.js - Busca
/api/_z/script.js
Lo que deberías ver:
- Estado: 200 ✅
- Dominio: Tu dominio de Pages (por ejemplo,
tu-proyecto.pages.dev) - Respuesta: Código JavaScript
Verificación 3: Modo estricto de Firefox (¡Lo más importante!)
Firefox tiene la protección de seguimiento más estricta. Si funciona en Firefox, funciona en todas partes.
- Abre el navegador Firefox
- Haz clic en el menú (☰) → Configuración
- Haz clic en Privacidad y seguridad en la barra lateral izquierda
- En "Protección mejorada contra el seguimiento", selecciona Estricta
- Visita tu sitio web
- Abre DevTools (F12) → Pestaña Network
- Recarga y verifica que
/api/_z/script.jsse carga con estado 200
Verificación 4: Panel de Zenovay
- Ve a app.zenovay.com e inicia sesión
- Haz clic en tu sitio web
- Visita tu sitio desplegado en otra pestaña
- En 1-2 minutos, deberías ver la visita aparecer en tu panel
Lista de verificación final
Antes de terminar, verifica TODOS estos elementos:
- El archivo
functions/api/_z/[[path]].tsexiste en la raíz del proyecto - La función aparece en la pestaña de funciones de Cloudflare Pages
- El script se carga en
/api/_z/script.jscon estado 200 - El atributo
data-tracking-codecontiene tu código de seguimiento correcto - Probado en Firefox con Protección mejorada contra el seguimiento establecida en Estricta
- Las visitas aparecen en el panel de Zenovay
Solución de problemas
La función devuelve 404
Causa: El archivo de función está en la ubicación incorrecta o tiene el nombre incorrecto.
Solución:
- Verifica que el archivo esté en exactamente
functions/api/_z/[[path]].ts- La carpeta
functionsdebe estar en la raíz del proyecto - Debe tener los subdirectorios
api/_z/ - Debe llamarse
[[path]].tscon doble corchete
- La carpeta
- Asegúrate de que la carpeta
functionsesté incluida en tu despliegue - Verifica la pestaña Functions en el panel de Cloudflare
La función devuelve 500
Causa: Hay un error de sintaxis o un error de tiempo de ejecución en el código de la función.
Solución:
- Revisa los registros de Functions en Cloudflare para obtener detalles del error:
- Ve a tu proyecto de Pages → Functions → Logs
- Verifica que todo el código se copió correctamente
- Asegúrate de que estés exportando
onRequest(noexport default)
Errores de TypeScript durante la compilación
Causa: Definiciones de tipo faltantes.
Solución: Opción 1: Instala tipos de Cloudflare Workers:
npm install --save-dev @cloudflare/workers-typesOpción 2: Usa la versión de JavaScript en su lugar (renombra a [[path]].js)
Errores de CORS en la consola
Causa: Los encabezados de CORS no se agregan correctamente.
Solución: Asegúrate de que tu función incluya:
- El manejador OPTIONS para solicitudes de verificación previa
responseHeaders.set('Access-Control-Allow-Origin', '*')en la respuesta
La geolocalización es incorrecta
Causa: La IP del cliente no se reenvía.
Solución: Verifica que tu función incluya:
const clientIP = request.headers.get('CF-Connecting-IP') || ''
proxyHeaders.set('X-Zenovay-Real-IP', clientIP)
El script se carga pero no hay datos en el panel
Causa: Desajuste de código de seguimiento.
Solución:
- Abre la Consola de DevTools (F12 → Pestaña Console) y busca errores
- Verifica que tu
data-tracking-codecoincida exactamente con lo que está en tu panel de Zenovay (¡sensible a mayúsculas!) - Asegúrate de que el dominio esté registrado en Zenovay
Próximos pasos
- Eventos personalizados - Seguimiento de interacciones de usuarios
- Identificación de visitantes - Vincula análisis a usuarios
- Solución de problemas - Más ayuda con problemas