Saltar al contenido principal
11 min de lectura

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
Tarjeta de script de seguimiento mostrando el fragmento de script con pestañas HTML, React, Next.js y Primera parte
La tarjeta de script de seguimiento bajo la configuración General de tu dominio — copia el código de la pestaña Primera parte.

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:

Terminal - Ejecuta desde la raíz de tu proyectoBash
# 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: .ts o .js

Esto crea una ruta "comodín" que maneja cualquier ruta después de /api/_z/.

¿TypeScript o JavaScript?

  • Usa .ts si tu proyecto usa TypeScript
  • Usa .js si 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:

functions/api/_z/[[path]].ts - Copia el archivo completoTypeScript
/**
* 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:

functions/api/_z/[[path]].js - Versión JavaScriptJavaScript
/**
* 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:

Script de seguimiento - Agrega a tu sección HTML <head>HTML
<!-- 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

app/layout.tsx o _app.tsxTSX
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

app.vue o nuxt.config.tsVUE
<!-- In app.vue -->
<script setup>
useHead({
script: [
  {
    src: '/api/_z/script.js',
    defer: true,
    'data-tracking-code': 'YOUR_TRACKING_CODE'
  }
]
})
</script>

Astro

src/layouts/Layout.astroASTRO
---
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

index.htmlHTML
<!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:

TerminalBash
git add .
git commit -m "Add Zenovay first-party tracking"
git push

Cloudflare Pages construirá e implementará automáticamente.

Opción B: Usando Wrangler CLI

TerminalBash
# Construir tu proyecto primero
npm run build

# Desplegar a Cloudflare Pages
npx wrangler pages deploy ./dist --project-name=your-project-name

Reemplaza ./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

  1. Ve a Cloudflare Dashboard → Pages
  2. Haz clic en tu proyecto
  3. Haz clic en la pestaña Functions
  4. 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 de src/)
  • Asegúrate de que el archivo se nombra [[path]].ts o [[path]].js
  • Verifica que el despliegue se completó exitosamente

Verificación 2: Pestaña de red en el navegador

  1. Abre tu sitio desplegado
  2. Presiona F12 (o Cmd+Option+I en Mac) para abrir DevTools
  3. Haz clic en la pestaña Network
  4. Recarga la página (Cmd+R o Ctrl+R)
  5. En el cuadro de filtro, escribe script.js
  6. 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.

  1. Abre el navegador Firefox
  2. Haz clic en el menú (☰) → Configuración
  3. Haz clic en Privacidad y seguridad en la barra lateral izquierda
  4. En "Protección mejorada contra el seguimiento", selecciona Estricta
  5. Visita tu sitio web
  6. Abre DevTools (F12) → Pestaña Network
  7. Recarga y verifica que /api/_z/script.js se carga con estado 200

Verificación 4: Panel de Zenovay

  1. Ve a app.zenovay.com e inicia sesión
  2. Haz clic en tu sitio web
  3. Visita tu sitio desplegado en otra pestaña
  4. 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]].ts existe 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.js con estado 200
  • El atributo data-tracking-code contiene 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:

  1. Verifica que el archivo esté en exactamente functions/api/_z/[[path]].ts
    • La carpeta functions debe estar en la raíz del proyecto
    • Debe tener los subdirectorios api/_z/
    • Debe llamarse [[path]].ts con doble corchete
  2. Asegúrate de que la carpeta functions esté incluida en tu despliegue
  3. 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:

  1. Revisa los registros de Functions en Cloudflare para obtener detalles del error:
    • Ve a tu proyecto de Pages → Functions → Logs
  2. Verifica que todo el código se copió correctamente
  3. Asegúrate de que estés exportando onRequest (no export default)

Errores de TypeScript durante la compilación

Causa: Definiciones de tipo faltantes.

Solución: Opción 1: Instala tipos de Cloudflare Workers:

TerminalBash
npm install --save-dev @cloudflare/workers-types

Opció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:

  1. El manejador OPTIONS para solicitudes de verificación previa
  2. 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:

  1. Abre la Consola de DevTools (F12 → Pestaña Console) y busca errores
  2. Verifica que tu data-tracking-code coincida exactamente con lo que está en tu panel de Zenovay (¡sensible a mayúsculas!)
  3. Asegúrate de que el dominio esté registrado en Zenovay

Próximos pasos

¿Fue útil esta página?