Le résultat d'abord
En 28 jours, baccalaureat.sn est passé à 2 150 clics (+80 %) et 31 800 impressions (+82 %) depuis la recherche Google, franchissant les paliers de 1 800 puis 2 000 clics. Voici le travail qui a produit ces chiffres.
1. Le rendu serveur, socle de tout
Le blocage initial : la plateforme était un SPA React, quasi invisible pour Googlebot (HTML vide au chargement). La migration vers Symfony + Turbo a tout changé — chaque page renvoie désormais du HTML complet, indexable immédiatement.
Vérification simple de ce que voit le crawler :
curl -A "Googlebot" https://baccalaureat.sn/annales | grep -c "annale-card"
# renvoie un nombre > 0 : le contenu est bien present dans le HTML servi,
# pas seulement injecte en JS apres coup2. Des pages ciblées sur l'intention de recherche
Les élèves cherchent des requêtes précises : « annales bac S1 Sénégal », « statistiques bac 2024 ». J'ai créé des pages dédiées par série, par matière, par année — chacune optimisée pour son intention.
#[Route('/annales/{serie}', name: 'annales_serie')]
public function parSerie(string $serie, AnnaleRepository $repo): Response
{
$annales = $repo->findBySerie($serie);
return $this->render('annales/serie.html.twig', [
'serie' => $serie,
'annales' => $annales,
'metaTitle' => "Annales {$serie} · Baccalauréat Sénégal",
'metaDesc' => "Toutes les épreuves série {$serie} du bac sénégalais, par matière et par année.",
]);
}3. Données structurées JSON-LD
Les données structurées aident Google à comprendre le contenu et débloquent les résultats enrichis.
{# templates/annales/show.html.twig #}
{% set jsonLd = {
'@context': 'https://schema.org',
'@type': 'LearningResource',
'name': annale.matiere ~ ' - ' ~ annale.serie ~ ' - ' ~ annale.annee,
'educationalLevel': annale.serie,
'inLanguage': 'fr',
'datePublished': annale.createdAt|date('c'),
} %}
<script type="application/ld+json">{{ jsonLd|json_encode|raw }}</script>4. Sitemap dynamique et robots
Un sitemap généré depuis la base, tenu à jour automatiquement, aide Google à découvrir chaque page.
#[Route('/sitemap.xml', name: 'sitemap', defaults: ['_format' => 'xml'])]
public function sitemap(AnnaleRepository $repo): Response
{
$urls = [];
foreach ($repo->findAllPublished() as $a) {
$urls[] = [
'loc' => $this->generateUrl('annale_show', ['slug' => $a->getSlug()], UrlGeneratorInterface::ABSOLUTE_URL),
'lastmod' => $a->getUpdatedAt()->format('c'),
];
}
$response = $this->render('sitemap.xml.twig', ['urls' => $urls]);
$response->headers->set('Content-Type', 'application/xml');
return $response;
}5. Mesurer et itérer
La Search Console est le tableau de bord du SEO. On y voit quelles pages montent (l'accueil : +386 %, les annales : +440 %), lesquelles stagnent, et sur quelles requêtes se positionner ensuite. Le SEO n'est pas un one-shot, c'est une boucle.
Ce que je retiens
- Le rendu serveur est un prérequis non négociable pour du contenu qui doit ranker.
- Une page = une intention de recherche précise. La granularité paie.
- Les données structurées JSON-LD aident Google à comprendre et enrichir l'affichage.
- Sitemap dynamique : chaque nouveau contenu est découvert vite.
- La Search Console pilote l'itération : on suit ce qui monte, on renforce.