Ce que tu vas trouver ici
- Le code complet d'un rotator PHP round-robin, avec verrou fichier pour eviter les collisions.
- La structure des fichiers : script principal + config JSON + pointeur JSON.
- Deploiement en 3 etapes sur n'importe quel hebergement mutualise.
- Extensions : compteurs par URL, quotas, fallback, forwarding query string.
- Alternative : si tu ne veux rien coder, farm.julienctr.fr fait le meme travail, gere pour toi, sur invitation.
Le concept en une phrase
Un rotator round-robin est un script serveur qui, a chaque requete recue, choisit la prochaine URL dans une liste et redirige le visiteur vers elle en HTTP 302. L'ordre est cyclique : URL 1, URL 2, URL 3, puis retour a la 1. Chaque URL recoit donc exactement la meme part du trafic, sans arbitrage ni favoritisme.
Cas d'usage typiques : repartir du trafic de test entre plusieurs pages, distribuer des visites publicitaires, faire du load balancing simple entre landings, ou executer un point d'entree unique qui masque les URLs finales.
Le code complet
Trois fichiers, tous dans le meme dossier. Le rotator lui-meme fait 30 lignes utiles. Aucune dependance externe, aucune base de donnees, aucun framework.
rotator.php<?php
// Rotator PHP round-robin equitable
// Chaque requete redirige vers la prochaine URL de la liste, en boucle.
$urls_file = __DIR__ . '/urls.json';
$pointer_file = __DIR__ . '/pointer.json';
$fallback_url = 'https://example.com/';
// Headers : jamais indexable, jamais cache
header('X-Robots-Tag: noindex, nofollow, noarchive, nosnippet');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Referrer-Policy: no-referrer');
// Charge la liste d'URLs
$urls = @json_decode(@file_get_contents($urls_file), true);
if (!is_array($urls) || empty($urls)) {
header('Location: ' . $fallback_url, true, 302);
exit;
}
// Lock exclusif sur un fichier sidecar pour eviter les collisions
$lock = fopen($pointer_file . '.lock', 'cb');
flock($lock, LOCK_EX);
// Lit le pointeur actuel, calcule le suivant, sauvegarde
$ptr = @json_decode(@file_get_contents($pointer_file), true) ?: ['idx' => -1];
$next = (((int)$ptr['idx']) + 1) % count($urls);
file_put_contents($pointer_file, json_encode(['idx' => $next]));
flock($lock, LOCK_UN);
fclose($lock);
// Cible + forwarding du query string
$target = $urls[$next];
$qs = $_SERVER['QUERY_STRING'] ?? '';
if ($qs !== '') {
$sep = (strpos($target, '?') === false) ? '?' : '&';
$target .= $sep . $qs;
}
header('Location: ' . $target, true, 302);
exit;
urls.json
[
"https://exemple.com/page-1",
"https://exemple.com/page-2",
"https://exemple.com/page-3"
]
pointer.json
{"idx": -1}
-1 initial est volontaire. Le premier hit calcule (-1 + 1) % N = 0, il tape donc bien la premiere URL de la liste. Sans cela, la premiere URL serait sautee au demarrage.Explique ligne par ligne
Les headers de securite (lignes 8-11)
X-Robots-Tag: noindex empeche Google (et les autres) d'indexer le rotator. Cache-Control: no-store empeche les CDN et navigateurs de mettre en cache la redirection : sinon le meme visiteur retomberait toujours sur la meme URL. Referrer-Policy: no-referrer masque l'origine cote cible.
Le chargement de la liste (lignes 14-18)
On lit urls.json, on decode. Si le fichier est absent ou vide, on redirige vers un fallback (par defaut la home d'exemple). C'est le filet de securite : le service ne tombe jamais en erreur 500.
Le verrou fichier (lignes 21-22)
flock(LOCK_EX) pose un verrou exclusif sur un fichier sidecar (pointer.json.lock). Si deux requetes arrivent en meme temps, la deuxieme attend que la premiere ait fini d'ecrire le pointeur. Sans ce verrou, on aurait des collisions et deux visiteurs pourraient recevoir la meme URL au lieu de deux differentes.
Le calcul du prochain index (lignes 25-27)
((int)$ptr['idx'] + 1) % count($urls) : on incremente et on prend le modulo pour boucler. Si la liste a 5 URLs et que l'index actuel est 4, le suivant est (4+1) % 5 = 0, retour a la premiere.
Le forwarding du query string (lignes 34-38)
Si l'URL entrante avait des parametres (?utm_source=email&campaign=aout), on les concatene a l'URL cible. Le separateur est ? si l'URL n'en avait pas, & sinon. Utile pour transmettre les UTM ou les identifiants de campagne.
La redirection finale (lignes 40-41)
Un simple Location: HTTP 302. Le 302 (temporaire) est prefere au 301 (permanent) car les navigateurs et proxies ne le mettent pas en cache aussi agressivement, ce qui garantit que chaque hit passe bien par le rotator.
Deploiement en 3 etapes
- Uploade les 3 fichiers (
rotator.php,urls.json,pointer.json) dans un sous-dossier de ton hebergement, par exemple/rotator/. Assure-toi que les fichiers JSON sont en permissions 644 et le dossier en 755. - Modifie
urls.jsonavec tes vraies URLs (une par ligne, entre guillemets, avec virgule apres chaque sauf la derniere). - Modifie
$fallback_urlen tete derotator.phpvers ta home ou une page qui a du sens comme fallback.
Ton rotator est actif a l'URL https://ton-domaine.tld/rotator/rotator.php. Pour raccourcir en /rotator/, renomme rotator.php en index.php.
curl -I. Chaque hit doit renvoyer un Location: different qui suit l'ordre de ta liste, puis boucler.Extensions possibles
Compteurs par URL
Ajoute un fichier counters.json qui stocke le nombre de hits par URL. Avant le header('Location: ...'), incremente le compteur correspondant. Meme pattern de verrou fichier.
Quotas et desactivation
Chaque URL a un quota max par jour. Filtre la liste avant round-robin en excluant celles qui ont atteint le plafond. Reset du compteur a minuit.
Fallback intelligent
Si toutes les URLs sont down (test de reachability), passe automatiquement au fallback. Un simple curl -I --max-time 2 avant redirection suffit.
Multi-liste avec categories
Au lieu d'un seul urls.json, plusieurs listes (urls-fr.json, urls-en.json, etc.) selectionnees selon un parametre ?list=fr dans l'URL entrante.
Passage a SQLite pour du volume
Au-dela de 100 hits/seconde, le lock fichier devient un goulot. Bascule sur SQLite avec une transaction : les lectures et ecritures sont bien plus rapides et supportent la concurrence sans verrou explicite.
L'alternative si tu ne veux rien coder
Si tu ne veux pas hebergsr le rotator toi-meme, gerer les fichiers JSON et deployer sur ton FTP, mon service farm.julienctr.fr fait exactement ce que fait ce code, avec en plus :
- Une interface web pour gerer ton URL (pas d'edition FTP)
- Des compteurs jour et cumul en temps reel
- Un historique quotidien sur 2 ans
- Gestion multi-membres avec repartition equitable entre tous
- Trafic interactif haute qualite envoye vers ton URL (visite + scroll + temps sur page depuis IPs residentielles)
Acces sur invitation par email a contact@julienctr.fr.
Questions frequentes
- Qu'est-ce qu'un rotator round-robin ?
- Un script serveur qui, a chaque requete recue, choisit la prochaine URL cible dans une liste et redirige le visiteur en HTTP 302. Round-robin signifie que la selection tourne dans l'ordre, puis boucle. Distribution equitable garantie.
- Pourquoi PHP plutot que Node.js ou Python ?
- Un rotator est un script minuscule (30 lignes utiles) et PHP tourne nativement sur 99 pour cent des hebergements mutualises sans configuration. Node.js ou Python demandent un runtime, un supervisor et un reverse proxy. Pour un rotator, PHP est le plus simple.
- Le code fonctionne-t-il en mutualise o2switch ou OVH ?
- Oui, sans modification. Aucune extension exotique : file_get_contents, json_decode, flock, header sont dans le noyau PHP depuis la version 5.0. Tourne sur o2switch, OVH, Hostinger, Infomaniak, PlanetHoster ou n'importe quel mutualise PHP 7 et plus.
- Comment gerer plusieurs milliers de hits par jour ?
- En dessous de 100 hits par seconde, le lock fichier tient sans probleme. Au-dela, bascule sur SQLite avec transaction, ou Redis. Pour 10 000 a 100 000 hits par jour, la version fichier decrite ici suffit largement.
- Le rotator est-il indexable par Google ?
- Non. Le code envoie X-Robots-Tag noindex nofollow et Cache-Control no-store. Google voit la 302 mais ne l'indexe pas et ne suit pas le lien vers la cible.
- Puis-je transferer les query strings a la cible ?
- Oui, le code inclut un bloc qui recupere le QUERY_STRING de la requete entrante et le concatene a l'URL cible avec le bon separateur. UTM et parametres de campagne sont preserves.
- Y a-t-il un service pret a l'emploi ?
- Oui, farm.julienctr.fr fait exactement cela, avec interface web, compteurs, historique et repartition entre membres. Acces sur invitation par email a contact@julienctr.fr.