Aller au contenu principal
11 min de lecture

Suivi propriétaire avec Cloudflare Pages

Configurez un proxy propriétaire à l'aide des fonctions Cloudflare Pages. C'est la meilleure option si vous hébergez déjà votre site sur Cloudflare Pages.

Difficulté : Facile - Il suffit d'ajouter un fichier à votre projet. Cela prend environ 5 minutes.

Pourquoi cela fonctionne (même origine)

Cette approche utilise /api/_z/script.js qui se trouve sur la même origine que votre site Web. Firefox ETP et autres protections de suivi ne bloquent que les requêtes inter-origines.

  • Le navigateur voit : votredomaine.com/api/_z/script.js → Même origine ✅
  • Votre fonction fait un proxy vers le serveur Zenovay de serveur à serveur (le navigateur ne voit jamais ceci)
  • Toutes les protections de suivi sont contournées car la requête est propriétaire

Avant de commencer

Assurez-vous d'avoir :

  • Un projet déployé sur Cloudflare Pages
  • Votre code de suivi Zenovay (trouvé dans Domaines, ouvrez votre site, puis l'onglet Général — dans la carte de script de suivi)
  • Un accès au code source de votre projet

Format de votre code de suivi

Votre code de suivi ressemble à : ZV_XXXXXXXXXX

  • Commence par ZV_
  • Suivi de 10 caractères (lettres et chiffres)
  • SENSIBLE À LA CASSE - copiez-le exactement
  • Exemple : ZV_Q8U0GYD70WR
Carte de script de suivi montrant l'extrait de script avec les onglets HTML, React, Next.js et Propriétaire
La carte du script de suivi dans les paramètres Général de votre domaine — copiez le code à partir de l'onglet Propriétaire.

Fonctionnement

Lorsque vous ajoutez un dossier functions à votre projet Cloudflare Pages, Cloudflare crée automatiquement des fonctions sans serveur. Nous allons créer une fonction qui fait un proxy des requêtes vers l'API de Zenovay, ce qui les fait apparaître comme des requêtes propriétaires aux navigateurs.

Navigateur → votredomaine.pages.dev/api/_z/script.js ✅ (Propriétaire)
               ↓
        Fonction Cloudflare
               ↓
         api.zenovay.com/fp/script.js (Serveur à serveur)

Étape 1 : Créer le répertoire des fonctions

L'emplacement du fichier est CRITIQUE

Le dossier functions DOIT être à la racine de votre projet. Pas à l'intérieur de src/, pas à l'intérieur de public/.

Emplacement correct :

votre-projet/
├── functions/          ← ICI (racine du projet)
│   └── api/
│       └── _z/
│           └── [[path]].ts
├── src/
├── public/
└── package.json

MAUVAIS emplacements :

  • src/functions/api/_z/[[path]].ts - Les fonctions ne fonctionneront pas
  • public/functions/api/_z/[[path]].ts - Les fonctions ne fonctionneront pas

Créez la structure de répertoires :

Terminal - Exécutez depuis la racine de votre projetBash
# Créer la structure de répertoires des fonctions
mkdir -p functions/api/_z

# Vérifiez que vous êtes au bon endroit
ls -la
# Vous devriez voir : functions/  src/  public/  package.json  etc.

Après avoir exécuté ces commandes, la structure de votre projet devrait ressembler à ceci :

votre-projet/
├── functions/
│   └── api/
│       └── _z/
│           └── [[path]].ts    ← Nous allons créer ceci ensuite
├── src/
├── public/
├── package.json
└── ...

Étape 2 : Créer la fonction proxy

Le nommage du fichier est IMPORTANT

Le fichier DOIT être nommé [[path]].ts (ou [[path]].js pour JavaScript) :

  • Deux crochets ouvrants : [[
  • Le mot path
  • Deux crochets fermants : ]]
  • Extension de fichier : .ts ou .js

Cela crée une route « fourre-tout » qui gère tout chemin après /api/_z/.

TypeScript ou JavaScript ?

  • Utilisez .ts si votre projet utilise TypeScript
  • Utilisez .js si votre projet utilise JavaScript
  • Les deux fonctionnent exactement de la même manière

Si vous n'êtes pas sûr, essayez d'abord .ts. Si vous recevez des erreurs TypeScript lors de la compilation, renommez-le en .js.

Créez le fichier functions/api/_z/[[path]].ts avec ce contenu :

functions/api/_z/[[path]].ts - Copiez ce fichier entièrementTypeScript
/**
* 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' },
  })
}
}

Alternative JavaScript

Si vous préférez JavaScript (pas de TypeScript), créez functions/api/_z/[[path]].js :

functions/api/_z/[[path]].js - Version 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' },
  })
}
}

Étape 3 : Ajouter le script de suivi

Ajoutez ce script au code HTML de votre site Web. L'emplacement dépend de votre framework :

Script de suivi - Ajoutez à votre section HTML <head>HTML
<!-- Zenovay Analytics - First-Party Tracking -->
<script defer
data-tracking-code="YOUR_TRACKING_CODE"
src="/api/_z/script.js">
</script>

Remplacez YOUR_TRACKING_CODE par votre code de suivi réel depuis le tableau de bord Zenovay (par exemple, ZV_Q8U0GYD70WR).

Exemples spécifiques à chaque framework

React / Next.js

app/layout.tsx ou _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 ou 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 simple

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>

Étape 4 : Déployer

Déployez votre projet sur Cloudflare Pages :

Option A : Utiliser Git (Recommandé)

Si vous avez des déploiements automatiques à partir de Git :

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

Cloudflare Pages compilera et déploiera automatiquement.

Option B : Utiliser la CLI Wrangler

TerminalBash
# Compilez d'abord votre projet
npm run build

# Déployez sur Cloudflare Pages
npx wrangler pages deploy ./dist --project-name=your-project-name

Remplacez ./dist par votre répertoire de sortie de compilation (pourrait être ./out, ./build, etc.) et your-project-name par le nom de votre projet Cloudflare Pages.


Étape 5 : Vérifier que cela fonctionne

Vérification 1 : Onglet Fonctions dans Cloudflare

  1. Allez à Cloudflare Dashboard → Pages
  2. Cliquez sur votre projet
  3. Cliquez sur l'onglet Functions
  4. Vous devriez voir api/_z/[[path]] listé

Vous ne voyez pas la fonction ?

  • Assurez-vous que le dossier functions/ est à la racine du projet (pas à l'intérieur de src/)
  • Assurez-vous que le fichier est nommé [[path]].ts ou [[path]].js
  • Vérifiez que le déploiement s'est terminé avec succès

Vérification 2 : Onglet Réseau dans le navigateur

  1. Ouvrez votre site déployé
  2. Appuyez sur F12 (ou Cmd+Option+I sur Mac) pour ouvrir les outils de développement
  3. Cliquez sur l'onglet Network
  4. Actualisez la page (Cmd+R ou Ctrl+R)
  5. Dans la zone de filtre, tapez script.js
  6. Recherchez /api/_z/script.js

Ce que vous devriez voir :

  • Statut : 200 ✅
  • Domaine : Votre domaine Pages (par exemple, votre-projet.pages.dev)
  • Réponse : Code JavaScript

Vérification 3 : Mode strict Firefox (Plus important !)

Firefox a la protection de suivi la plus stricte. Si cela fonctionne dans Firefox, cela fonctionne partout.

  1. Ouvrez le navigateur Firefox
  2. Cliquez sur le menu (☰) → Paramètres
  3. Cliquez sur Confidentialité et sécurité dans la barre latérale gauche
  4. Sous « Protection renforcée contre le suivi », sélectionnez Strict
  5. Visitez votre site Web
  6. Ouvrez les outils de développement (F12) → onglet Réseau
  7. Actualisez et vérifiez que /api/_z/script.js se charge avec le statut 200

Vérification 4 : Tableau de bord Zenovay

  1. Allez à app.zenovay.com et connectez-vous
  2. Cliquez sur votre site Web
  3. Visitez votre site déployé dans un autre onglet
  4. Dans 1-2 minutes, vous devriez voir la visite apparaître dans votre tableau de bord

Liste de vérification finale

Avant de terminer, vérifiez TOUS les éléments suivants :

  • Le fichier functions/api/_z/[[path]].ts existe à la racine du projet
  • La fonction apparaît dans l'onglet Fonctions de Cloudflare Pages
  • Le script se charge à /api/_z/script.js avec le statut 200
  • L'attribut data-tracking-code contient votre code de suivi correct
  • Testé dans Firefox avec la protection renforcée contre le suivi définie sur Strict
  • Les visites apparaissent dans le tableau de bord Zenovay

Dépannage

La fonction retourne 404

Cause : Le fichier de fonction est au mauvais emplacement ou a le mauvais nom.

Solution :

  1. Vérifiez que le fichier se trouve exactement à functions/api/_z/[[path]].ts
    • Le dossier functions doit être à la racine du projet
    • Doit avoir les sous-répertoires api/_z/
    • Doit être nommé [[path]].ts avec des crochets doubles
  2. Assurez-vous que le dossier functions est inclus dans votre déploiement
  3. Vérifiez l'onglet Fonctions du tableau de bord Cloudflare

La fonction retourne 500

Cause : Il y a une erreur de syntaxe ou une erreur d'exécution dans le code de la fonction.

Solution :

  1. Vérifiez les journaux des fonctions dans Cloudflare pour plus de détails sur les erreurs :
    • Allez à votre projet Pages → Functions → Logs
  2. Vérifiez que tout le code a été copié correctement
  3. Assurez-vous que vous exportez onRequest (pas export default)

Erreurs TypeScript pendant la compilation

Cause : Définitions de type manquantes.

Solution : Option 1 : Installez les types Cloudflare Workers :

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

Option 2 : Utilisez la version JavaScript à la place (renommez en [[path]].js)

Erreurs CORS dans la console

Cause : Les en-têtes CORS ne sont pas ajoutés correctement.

Solution : Assurez-vous que votre fonction inclut les deux éléments suivants :

  1. Le gestionnaire OPTIONS pour les requêtes de préalable
  2. responseHeaders.set('Access-Control-Allow-Origin', '*') dans la réponse

La géolocalisation est incorrecte

Cause : L'IP client n'est pas transmise.

Solution : Vérifiez que votre fonction inclut :

const clientIP = request.headers.get('CF-Connecting-IP') || ''
proxyHeaders.set('X-Zenovay-Real-IP', clientIP)

Le script se charge mais aucune donnée dans le tableau de bord

Cause : Décalage du code de suivi.

Solution :

  1. Ouvrez la console des outils de développement (F12 → onglet Console) et recherchez les erreurs
  2. Vérifiez que votre data-tracking-code correspond exactement à ce qui se trouve dans votre tableau de bord Zenovay (sensible à la casse !)
  3. Assurez-vous que le domaine est enregistré dans Zenovay

Prochaines étapes

Cette page vous a-t-elle été utile ?