Collecte côté serveur
La plupart des robots n’exécutent pas le JavaScript : seul votre serveur les voit. Une clé d’API et quelques lignes suffisent pour les compter, ainsi que les événements que seul le serveur connaît.
Sur cette page
Pourquoi côté serveur#
Deux usages :
- Les robots : Googlebot, GPTBot, ClaudeBot, les outils SEO… lisent vos pages sans charger
k.js. Votre serveur transmet leurs passages, qui alimentent le rapport Robots. - Les événements serveur : paiement confirmé par webhook, abonnement, commande saisie en administration. Transmis avec l’IP et le User-Agent du visiteur, ils rejoignent la visite commencée dans son navigateur.
Sur WordPress, le plugin fait déjà tout cela : cette page concerne les autres sites.
Créer une clé d’API#
- Ouvrez la collecte côté serveur
Installation › Collecte côté serveur › Clés d’API (propriétaires et administrateurs du site).
- Nommez la clé et créez-la
Par exemple « Serveur de production ». Elle commence par
ki_live_. - Copiez-la tout de suite
Elle ne s’affiche qu’une fois : seule son empreinte est conservée.
- Rangez-la dans une variable d’environnement
KREOMNIS_API_KEY, sur le serveur uniquement.
Jamais dans le code envoyé au navigateur, jamais dans une variable préfixée NEXT_PUBLIC_, jamais dans un fichier servi par le web ni dans un dépôt de code. Une clé exposée se révoque dans la même page : elle cesse de fonctionner en 10 secondes au plus. Vingt clés actives au plus par site.
Next.js#
Un seul fichier à la racine du projet (ou dans src/) : middleware.ts pour Next.js 13 à 15, proxy.ts pour Next.js 16. L’envoi part en arrière-plan (event.waitUntil) : la réponse n’attend jamais Kreomnis. Seuls les robots sont transmis, sans la partie ?… de l’adresse.
// middleware.ts (Next.js 13 à 15) ou proxy.ts (Next.js 16), à la racine du projet (ou dans src/).
// Les robots n'exécutent pas le JavaScript : on les compte côté serveur, sans ralentir la réponse.
// Clé : KREOMNIS_API_KEY dans les variables d'environnement du serveur (jamais préfixée NEXT_PUBLIC_).
// Vous avez déjà un middleware ? Copiez kreomnisBots et appelez-la au début du vôtre.
import { NextResponse, type NextFetchEvent, type NextRequest } from 'next/server';
const KREOMNIS_API = 'https://kreomnisindexation.com/api/v1/events';
const BOT = /bot|crawl|spider|slurp|preview|fetch|scan|monitor|facebookexternalhit|whatsapp|curl|wget|python|go-http|java\/|okhttp|axios|headless|gpt|claude|perplexity|bytespider|ahrefs|semrush|lighthouse/i;
function kreomnisBots(req: NextRequest, event: NextFetchEvent) {
const key = process.env.KREOMNIS_API_KEY;
const ua = req.headers.get('user-agent') || '';
if (!key || (ua && !BOT.test(ua))) return;
const host = req.headers.get('x-forwarded-host') || req.headers.get('host') || req.nextUrl.host;
const ip = (req.headers.get('x-real-ip') || req.headers.get('x-forwarded-for')?.split(',')[0] || '').trim() || null;
event.waitUntil(fetch(KREOMNIS_API, {
method: 'POST',
headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
body: JSON.stringify({ name: 'pageview', url: `https://${host}${req.nextUrl.pathname}`, user_agent: ua, ip, method: req.method }),
signal: typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(3000) : undefined,
}).then(() => {}, () => {}));
}
export default function proxy(req: NextRequest, event: NextFetchEvent) {
kreomnisBots(req, event);
return NextResponse.next();
}
// Fichiers statiques et internes exclus : seules les pages passent par ici.
export const config = {
matcher: ['/((?!_next/static|_next/image|api/|favicon.ico|.*\\.(?:png|jpe?g|gif|webp|avif|svg|ico|css|js|map|woff2?|ttf|mp4|webm|pdf)$).*)'],
};Vous avez déjà un middleware ? Copiez la fonction kreomnisBots et appelez-la au début du vôtre.
Node.js et Express#
Pour Express 4 ou 5 (Node 18 et plus). Le passage du robot est envoyé une fois la réponse terminée (res.on('finish')), avec un délai de 3 secondes au plus et les erreurs ignorées. Derrière un proxy ou un CDN, activez app.set('trust proxy', true) pour que req.ip soit celle du visiteur.
// Express 4 ou 5 (Node 18+) : robots et événements serveur, envoyés après la réponse.
// Clé : KREOMNIS_API_KEY dans les variables d'environnement du serveur, jamais dans le code navigateur.
// Derrière un proxy ou un CDN : app.set('trust proxy', true) pour que req.ip soit celle du visiteur.
const KREOMNIS_API = 'https://kreomnisindexation.com/api/v1/events';
const KREOMNIS_BOT = /bot|crawl|spider|slurp|preview|fetch|scan|monitor|facebookexternalhit|whatsapp|curl|wget|python|go-http|java\/|okhttp|axios|headless|gpt|claude|perplexity|bytespider|ahrefs|semrush|lighthouse/i;
function kreomnis(event) {
const key = process.env.KREOMNIS_API_KEY;
if (!key) return;
try {
fetch(KREOMNIS_API, {
method: 'POST',
headers: { Authorization: `Bearer ${key}`, 'Content-Type': 'application/json' },
body: JSON.stringify(event),
signal: AbortSignal.timeout(3000),
}).catch(() => {});
} catch (e) { /* jamais d'erreur visible pour le site */ }
}
app.use((req, res, next) => {
const ua = req.get('user-agent') || '';
if (!ua || KREOMNIS_BOT.test(ua)) {
// Origine et chemin seulement : la partie ?… peut contenir des données personnelles.
res.on('finish', () => kreomnis({
name: 'pageview', url: `${req.protocol}://${req.get('host')}${req.originalUrl.split('?')[0]}`,
user_agent: ua, ip: req.ip, status: res.statusCode, method: req.method,
}));
}
next();
});
// Achat confirmé côté serveur (ip et user_agent du visiteur pour rattacher sa visite) :
function kreomnisAchat(req, commande) {
kreomnis({
name: 'purchase', url: `${req.protocol}://${req.get('host')}/merci`,
ip: req.ip, user_agent: req.get('user-agent'), order_id: String(commande.id),
revenue: { amount: commande.total, currency: 'EUR' }, items: commande.lignes,
});
}PHP#
Pour un site PHP sans WordPress (PHP 7.4 et plus, extension curl), en tête de index.php. La page part d’abord (fastcgi_finish_request ou litespeed_finish_request quand ils existent), l’appel à Kreomnis se fait ensuite, avec un délai court.
<?php
// En tête de votre index.php (site PHP sans WordPress) : compte les robots, après l'envoi de la page.
// Clé : variable d'environnement KREOMNIS_API_KEY (jamais dans un fichier servi par le web).
if (!function_exists('kreomnis_event')) {
function kreomnis_event(array $event) {
$key = getenv('KREOMNIS_API_KEY') ?: ($_SERVER['KREOMNIS_API_KEY'] ?? '');
if (!$key || !function_exists('curl_init')) {
return;
}
try {
$ch = curl_init('https://kreomnisindexation.com/api/v1/events');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $key, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode($event, JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_NOSIGNAL => true,
CURLOPT_CONNECTTIMEOUT_MS => 300,
CURLOPT_TIMEOUT_MS => 800,
]);
curl_exec($ch);
if (PHP_VERSION_ID < 80000) {
curl_close($ch);
}
} catch (\Throwable $e) {
// jamais d'erreur visible pour le site
}
}
}
$kreomnis_ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
if ($kreomnis_ua === '' || preg_match('~bot|crawl|spider|slurp|preview|fetch|scan|monitor|facebookexternalhit|whatsapp|curl|wget|python|go-http|java/|okhttp|axios|headless|gpt|claude|perplexity|bytespider|ahrefs|semrush|lighthouse~i', $kreomnis_ua)) {
register_shutdown_function(function () use ($kreomnis_ua) {
$status = http_response_code();
// La page part d'abord ; l'appel à Kreomnis se fait ensuite, hors du temps de réponse.
if (function_exists('fastcgi_finish_request')) {
fastcgi_finish_request();
} elseif (function_exists('litespeed_finish_request')) {
litespeed_finish_request();
} else {
while (ob_get_level() > 0) {
@ob_end_flush();
}
flush();
}
// Origine et chemin seulement : la partie ?… peut contenir des données personnelles.
$https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
kreomnis_event([
'name' => 'pageview',
'url' => ($https ? 'https' : 'http') . '://' . ($_SERVER['HTTP_HOST'] ?? '') . strtok($_SERVER['REQUEST_URI'] ?? '/', '?'),
'user_agent' => $kreomnis_ua,
'ip' => $_SERVER['REMOTE_ADDR'] ?? null,
'status' => $status ?: 200,
'method' => $_SERVER['REQUEST_METHOD'] ?? 'GET',
]);
});
}Autres langages (HTTP)#
Tout langage capable d’envoyer une requête HTTP peut utiliser l’API. Exemple d’achat confirmé côté serveur :
# Clé dans une variable d'environnement (jamais en clair dans un script versionné ni côté navigateur)
curl -X POST https://kreomnisindexation.com/api/v1/events \
-H "Authorization: Bearer $KREOMNIS_API_KEY" \
-H "Content-Type: application/json" \
--max-time 3 \
-d '{
"name": "purchase",
"url": "https://VOTRE-SITE/merci",
"ip": "IP_DU_VISITEUR",
"user_agent": "USER_AGENT_DU_VISITEUR",
"order_id": "A-1001",
"revenue": { "amount": 42.5, "currency": "EUR" },
"items": [{ "id": "SKU-42", "name": "Pain au levain", "price": 4.5, "quantity": 2 }]
}'
# Lot : { "events": [ … ] } (100 au plus, 512 ko au plus). Robots : "name": "pageview" avec leur user_agent et "status".
# N'envoyez que l'origine et le chemin de l'URL : la partie ?… peut contenir des données personnelles.Champs, limites et réponses : voir la référence de l’API.
Bonnes pratiques#
- Transmettez l’IP et le User-Agent du visiteur (champs
ipetuser_agent) : sans eux, ce sont l’IP et le User-Agent de votre serveur qui sont utilisés : l’événement ne rejoint pas la visite, et une page vue est en général classée robot. - N’envoyez que l’origine et le chemin de l’adresse : la partie
?…peut contenir des données personnelles. Seuls les paramètresutm_*sont exploités. - Pages vues serveur : seulement les robots si le traceur est installé. Une page vue serveur avec un User-Agent de navigateur est comptée dans l’audience, et ferait doublon avec celle du traceur.
- Délai court et erreurs ignorées : comme dans les extraits, l’envoi ne doit jamais retarder ni faire échouer vos pages.
Une question sans réponse ici ? Consultez les questions fréquentes.