Aller au contenu principal
Zenovay
Gratuit25 minutesAvancé

Suivi côté serveur

Envoyez des événements et des pages vues directement à l'endpoint de suivi Zenovay depuis votre serveur pour une immunité contre les bloqueurs de publicités et un contrôle total de la collecte.

server-sideapitrackingbackendprivacy
Dernière mise à jour :

Envoyez les événements d'analyse directement depuis votre backend vers Zenovay. Le suivi côté serveur est immunisé contre les bloqueurs de publicités, vous permet d'enregistrer les événements qui se produisent uniquement côté serveur (achats confirmés, abonnements pilotés par webhook), et vous donne un contrôle total sur ce qui est envoyé.

Pourquoi le suivi côté serveur ?

Avantages

AvantageDescription
Immunité contre les bloqueurs de publicitésLes événements atteignent Zenovay même lorsque le script client est bloqué par le navigateur
Événements côté serveur uniquementSuivez les choses qui se produisent côté serveur — paiements confirmés, tâches cron, webhooks
Contrôle des donnéesVous décidez exactement ce qui est envoyé
Suivi hybrideCombinez avec le script client pour une couverture complète

Quand utiliser

  • Confirmation d'achat e-commerce (après confirmation du prestataire de paiement)
  • Événements backend (abonnement créé, abonnement annulé)
  • Événements pilotés par webhook (Stripe, prestataires de paiement)
  • Suivi hybride (script client pour la navigation + serveur pour les conversions confirmées)

Endpoint de suivi

Les requêtes côté serveur utilisent le même endpoint d'ingestion que le script client. Envoyez un POST à l'endpoint de suivi avec le code de suivi de votre site dans le chemin :

POST https://api.zenovay.com/e/{trackingCode}

Il s'agit d'un endpoint public, non authentifié — il ne nécessite pas de clé API et il est ouvert sur tous les plans (c'est le même chemin vers lequel le script de suivi envoie). Le code de suivi est la même valeur que vous mettez dans l'attribut data-tracking-code de votre snippet d'installation.

Info

L'endpoint d'ingestion est partagé avec le suivi du navigateur, donc sa charge utile est structurée comme les données qu'un navigateur enverrait. Cela signifie que quelques champs que le navigateur remplit automatiquement — session_id, device_type, browser, os et user_agent — doivent être fournis par vous dans le corps JSON lors de l'appel depuis un serveur. Les requêtes auxquelles il manque ces éléments sont rejetées.

Champs requis

Chaque événement que vous envoyez doit inclure ces champs dans le corps JSON :

ChampRemarques
session_idUne chaîne de au moins 8 caractères. Réutilisez la même valeur pour les événements appartenant à la même visite.
urlUne URL complète et valide (par exemple https://example.com/checkout/success).
device_typepar exemple desktop, mobile, tablet ou server.
browserUn nom de navigateur/client. Utilisez quelque chose de descriptif pour le trafic serveur (par exemple server).
osUn nom de système d'exploitation (par exemple Linux).
user_agentUne chaîne user-agent. Évitez les agents génériques ressemblant à des bots (curl, python, wget, etc.) — ils sont filtrés comme bots.

visitor_id est optionnel mais recommandé (≥8 caractères) pour que les événements répétés se lient au même visiteur.

Page vue vs. Événement personnalisé

Le type d'événement est défini avec le champ event_type :

  • Page vue — omettez event_type (par défaut) ou définissez-le sur pageview.
  • Événement personnalisé — définissez event_type sur custom, mettez le nom de l'événement dans event_name et toutes les métadonnées dans properties.
# Événement personnalisé (par exemple un achat confirmé)
curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "custom",
    "event_name": "purchase",
    "url": "https://example.com/checkout/success",
    "referrer": "https://example.com/cart",
    "session_id": "srv-9f2a7c41bd",
    "device_type": "server",
    "browser": "server",
    "os": "Linux",
    "user_agent": "MyApp-Server/1.0",
    "properties": {
      "order_id": "ORD-12345",
      "value": 99.99,
      "currency": "USD"
    }
  }'
# Page vue
curl -X POST "https://api.zenovay.com/e/YOUR_TRACKING_CODE" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type": "pageview",
    "url": "https://example.com/products/widget",
    "referrer": "https://google.com/search",
    "session_id": "srv-9f2a7c41bd",
    "device_type": "server",
    "browser": "server",
    "os": "Linux",
    "user_agent": "MyApp-Server/1.0"
  }'

Remarque sur la géolocalisation

Pour le trafic du navigateur, Zenovay dérive l'emplacement de l'IP du visiteur qui se connecte. Lorsque vous appelez l'endpoint depuis votre serveur, l'IP de connexion est celle de votre serveur — donc les événements seront géolocalisés vers votre infrastructure, pas l'utilisateur final.

Si vous avez besoin d'une géolocalisation précise par visiteur, effectuez ce suivi côté client (le snippet d'installation standard) ou utilisez le proxy first-party, et réservez le suivi côté serveur pour les événements backend (achats, abonnements) où l'emplacement exact de l'utilisateur n'est pas l'objectif. L'ajout d'un header X-Forwarded-For sur un simple appel serveur ne remplace pas l'IP du serveur pour l'endpoint public.

Exemples de mise en œuvre

Node.js / Express

// lib/analytics.js
const TRACKING_CODE = process.env.ZENOVAY_TRACKING_CODE;
const ENDPOINT = `https://api.zenovay.com/e/${TRACKING_CODE}`;

// Champs de base que chaque événement côté serveur a besoin.
const SERVER_DEFAULTS = {
  device_type: 'server',
  browser: 'server',
  os: 'Linux',
  user_agent: 'MyApp-Server/1.0'
};

async function trackEvent(eventName, { url, sessionId, properties = {} }) {
  const payload = {
    event_type: 'custom',
    event_name: eventName,
    url,
    session_id: sessionId,
    properties,
    ...SERVER_DEFAULTS
  };

  try {
    const response = await fetch(ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    return response.ok;
  } catch (error) {
    console.error('Analytics error:', error);
    return false;
  }
}

async function trackPageview({ url, sessionId, referrer = '' }) {
  const payload = {
    event_type: 'pageview',
    url,
    referrer,
    session_id: sessionId,
    ...SERVER_DEFAULTS
  };

  try {
    const response = await fetch(ENDPOINT, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(payload)
    });
    return response.ok;
  } catch (error) {
    console.error('Pageview tracking error:', error);
    return false;
  }
}

module.exports = { trackEvent, trackPageview };

Python (Flask)

# analytics.py
import os
import requests
from flask import request

TRACKING_CODE = os.getenv('ZENOVAY_TRACKING_CODE')
TRACKING_URL = f'https://api.zenovay.com/e/{TRACKING_CODE}'

SERVER_DEFAULTS = {
    'device_type': 'server',
    'browser': 'server',
    'os': 'Linux',
    'user_agent': 'MyApp-Server/1.0',
}

def track_event(event_name, session_id, url, properties=None):
    payload = {
        'event_type': 'custom',
        'event_name': event_name,
        'url': url,
        'session_id': session_id,
        'properties': properties or {},
        **SERVER_DEFAULTS,
    }
    try:
        response = requests.post(TRACKING_URL, json=payload, timeout=5)
        return response.ok
    except Exception as e:
        print(f'Analytics error: {e}')
        return False

def track_pageview(session_id, url, referrer=''):
    payload = {
        'event_type': 'pageview',
        'url': url,
        'referrer': referrer,
        'session_id': session_id,
        **SERVER_DEFAULTS,
    }
    try:
        response = requests.post(TRACKING_URL, json=payload, timeout=5)
        return response.ok
    except Exception as e:
        print(f'Pageview tracking error: {e}')
        return False

PHP

<?php
class ZenovayAnalytics {
    private $trackingUrl;
    private $defaults = [
        'device_type' => 'server',
        'browser'     => 'server',
        'os'          => 'Linux',
        'user_agent'  => 'MyApp-Server/1.0',
    ];

    public function __construct($trackingCode) {
        $this->trackingUrl = "https://api.zenovay.com/e/{$trackingCode}";
    }

    public function trackEvent($eventName, $sessionId, $url, $properties = []) {
        $payload = array_merge([
            'event_type' => 'custom',
            'event_name' => $eventName,
            'url'        => $url,
            'session_id' => $sessionId,
            'properties' => $properties,
        ], $this->defaults);
        return $this->sendRequest($payload);
    }

    public function trackPageview($sessionId, $url, $referrer = '') {
        $payload = array_merge([
            'event_type' => 'pageview',
            'url'        => $url,
            'referrer'   => $referrer,
            'session_id' => $sessionId,
        ], $this->defaults);
        return $this->sendRequest($payload);
    }

    private function sendRequest($payload) {
        $ch = curl_init($this->trackingUrl);
        curl_setopt_array($ch, [
            CURLOPT_POST => true,
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT => 5
        ]);
        curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        return $httpCode >= 200 && $httpCode < 300;
    }
}

// Utilisation
$analytics = new ZenovayAnalytics(getenv('ZENOVAY_TRACKING_CODE'));
$analytics->trackEvent('purchase', 'srv-9f2a7c41bd', 'https://example.com/checkout/success', [
    'order_id' => 'ORD-12345',
    'value'    => 99.99
]);

Suivi hybride

Le modèle le plus courant : suivez la navigation avec le script client et confirmez les conversions depuis le serveur une fois que le prestataire de paiement les a confirmées.

// Côté client : suivez l'intention avec le script Zenovay
window.zenovay('track', 'checkout_started');

// Côté serveur : suivez l'événement confirmé depuis votre gestionnaire webhook
app.post('/webhooks/stripe', async (req, res) => {
  const event = req.body;

  if (event.type === 'checkout.session.completed') {
    await trackEvent('purchase', {
      url: 'https://example.com/checkout/success',
      sessionId: 'srv-' + event.data.object.id.slice(-12),
      properties: {
        order_id: event.data.object.id,
        value: event.data.object.amount_total / 100
      }
    });
  }

  res.sendStatus(200);
});

Filtrage des bots

Zenovay filtre déjà le trafic bot évidemment sur le côté ingestion, donc les événements envoyés avec des agents génériques comme curl, wget, python ou tout ce qui correspond aux modèles de crawler courants sont rejetés. C'est pourquoi les exemples ci-dessus utilisent un user_agent descriptif pour votre serveur.

Si vous transmettez également de vraies requêtes client via votre backend, filtrez les crawlers avant d'envoyer pour ne pas brûler votre limite de débit sur le trafic qui serait rejeté de toute façon :

function isBot(userAgent) {
  const botPatterns = [
    /bot/i, /crawler/i, /spider/i, /scraper/i,
    /curl/i, /wget/i, /python/i, /java\//i,
    /googlebot/i, /bingbot/i, /yandex/i
  ];
  return botPatterns.some(pattern => pattern.test(userAgent || ''));
}

Limites de débit

L'endpoint de suivi a des limites de débit par adresse IP :

  • Limite de rafale : 60 requêtes par 10 secondes
  • Limite durable : 5 000 requêtes par heure

Ces limites s'appliquent spécifiquement à l'endpoint d'ingestion de suivi, pas à l'API REST (qui est une fonctionnalité payante séparée avec ses propres limites).

Dépannage

Les événements n'apparaissent pas

Vérifiez :

  • Le code de suivi dans le chemin URL est correct.
  • Tous les champs requis sont présents (session_id de 8+ caractères, url valide, device_type, browser, os, user_agent). Les champs manquants retournent un 400 avec une liste de ce qui manque.
  • Votre user_agent n'est pas filtré comme un bot (évitez curl, python, etc.).
  • L'IP n'est pas limitée en débit.

Événements en double

Assurez-vous :

  • Vous ne suivez pas le même événement à la fois côté client et côté serveur.
  • Les gestionnaires webhook ne déclenchent pas plus d'une fois pour le même événement.

La géolocalisation reflète votre serveur

C'est normal pour les appels côté serveur — l'IP de connexion est votre serveur, pas le visiteur. Utilisez le script client (ou un proxy first-party) pour une géolocalisation précise du visiteur et utilisez le suivi côté serveur pour les événements backend où l'emplacement n'est pas l'objectif.

Étapes suivantes

Cet article vous a-t-il aidé ?