WordPress qui gère le contenu, Astro qui affiche des pages ultra-rapides : sur le papier, le mariage a tout pour plaire. Mais entre le choix REST API vs GraphQL, les previews qui plantent et le cache mal configuré, le chemin vers un headless propre est semé d’embûches. Dans cet article, on décortique l’architecture, le code et surtout les pièges que personne ne mentionne avant qu’on tombe dedans.
Comprendre l’architecture headless avec WordPress et Astro
Avant de se lancer tête baissée dans le headless, il faut comprendre ce qui change vraiment par rapport à un WordPress classique. Ce n’est pas juste « installer un plugin en plus » : c’est repenser toute la façon dont le contenu circule entre l’admin et le visiteur. Voyons ça calmement, brique par brique.
Le principe du découplage backend/frontend
Dans un WordPress traditionnel, tout est mélangé : le thème PHP génère le HTML, la base de données stocke le contenu, et tout ça tourne sur le même serveur, au même moment. Avec le headless, on sépare les deux couches.
WordPress devient un backend pur : il gère les articles, les pages, les médias, les utilisateurs, via son admin qu’on connaît tous. Mais il n’affiche plus rien côté visiteur. Sa seule mission, c’est d’exposer les données proprement, via l’API REST native de WordPress ou via WPGraphQL (souvent préférable pour des requêtes plus précises et moins gourmandes).
Le frontend, lui, est confié à un framework externe, ici Astro, qui va récupérer ces données et générer les pages. Cette séparation a un nom : le découplage. Et c’est le cœur de toute architecture headless.
Où se situe Astro dans ce schéma
Astro joue le rôle du « consommateur » de données. Il vient interroger WordPress au moment du build (ou à la demande, on y revient juste après), récupère le JSON renvoyé par l’API, et transforme tout ça en HTML statique optimisé.
Concrètement, on utilise généralement fetch() dans les composants Astro pour appeler l’API REST ou WPGraphQL, puis on injecte les données dans le template. Là où Astro tire vraiment son épingle du jeu, c’est avec le concept d’Astro Islands : le site reste en HTML statique par défaut (donc ultra rapide), et on n’ajoute du JavaScript interactif que là où c’est vraiment nécessaire (un formulaire, un carrousel, un panier). Pas de framework JS chargé en bloc sur toute la page, contrairement à ce qu’on voit souvent avec React ou Vue en frontend headless.
Schéma explicatif d’une stack headless WP + Astro
Pour visualiser la chaîne complète, voici comment ça s’articule (un schéma serait idéal ici, mais on va le décrire) :
- L’utilisateur arrive sur le site et charge une page.
- Astro (hébergé séparément, par exemple sur Vercel, Netlify, ou même Infomaniak) sert le HTML déjà généré, ou déclenche une génération à la demande.
- Pendant la phase de build (ou lors d’une requête SSR), Astro fait des appels API vers WordPress.
- WordPress (backend, base de données MySQL, interface d’admin) répond avec les données demandées : articles, taxonomies, champs personnalisés (via ACF par exemple).
Deux hébergements distincts, donc : WordPress reste chez un hébergeur classique type Infomaniak ou o2switch, pendant qu’Astro peut vivre ailleurs, souvent sur une plateforme orientée Jamstack.
Quand le headless a vraiment du sens (et quand non)
Bon, soyons honnêtes : le headless n’est pas la solution miracle qu’on nous vend parfois. Ça a du sens dans certains contextes précis :
- Performance critique : sites à fort trafic où chaque milliseconde compte (e-commerce, médias).
- Séparation des équipes : une équipe backend gère WordPress, une équipe frontend développe avec Astro, sans se marcher dessus.
- Multi-frontend : le même contenu WordPress alimente un site web, une appli mobile, un espace membre. Un seul backend, plusieurs sorties.
Par contre, pour un petit site vitrine ou un blog classique, c’est souvent overkill. La complexité ajoutée (deux hébergements, gestion des builds, debug plus technique) ne se justifie pas si les gains de performance sont marginaux. Et surtout : attention aux plugins qui dépendent du rendu PHP classique. Certains page builders (Elementor, Divi) perdent tout leur intérêt en headless, puisqu’ils génèrent du HTML côté serveur que votre frontend Astro n’utilisera jamais. Avant de foncer, posez-vous la question : ai-je vraiment besoin de cette architecture, ou est-ce que je complexifie pour rien ?
REST API native vs WPGraphQL : quelle solution choisir pour Astro
Une fois l’architecture headless posée, reste une question centrale : comment Astro va récupérer les données de WordPress ? Deux options s’affrontent, et honnêtement, le choix n’est pas anodin sur le long terme. Détaillons les deux approches.
REST API WordPress : simplicité et limites
La REST API, c’est le choix « zéro configuration ». Elle est disponible par défaut sur toute installation WordPress depuis la version 4.7 : pas de plugin à installer, pas de setup particulier. Les endpoints sont prévisibles et bien documentés (/wp-json/wp/v2/posts, /wp-json/wp/v2/pages, etc.), ce qui facilite grandement la prise en main pour un premier projet Astro.
Le problème, c’est l’over-fetching. Quand on appelle /wp-json/wp/v2/posts, on récupère tout : le contenu HTML complet, l’excerpt, les métadonnées, les liens vers les ressources associées… même si on n’a besoin que du titre pour une liste d’articles. Résultat : des payloads lourds pour pas grand-chose.
Autre limitation : les données liées (auteur, catégories, champs ACF) nécessitent souvent des appels supplémentaires, sauf à activer le paramètre _embed (qui alourdit encore la réponse) ou à installer des plugins comme ACF to REST API pour exposer les champs personnalisés.
WPGraphQL : requêtes précises et écosystème
WPGraphQL change complètement la donne. Il faut installer le plugin (gratuit, activement maintenu), mais une fois en place, on peut cibler exactement les champs voulus, en une seule requête. Fini l’over-fetching : on demande le titre, l’extrait et l’image mise en avant, on reçoit uniquement ça.
La gestion des relations est aussi beaucoup plus propre. Besoin de l’auteur, de ses catégories et de trois articles liés ? Une seule requête GraphQL suffit, avec la structure imbriquée qu’on souhaite. Couplé à WPGraphQL for ACF, on récupère aussi les champs personnalisés sans bricolage.
Le revers de la médaille : la courbe d’apprentissage est plus raide (il faut comprendre les concepts de schema, de query, de resolver), et l’écosystème de plugins tiers est parfois moins mature que celui de la REST API, qui bénéficie de dix ans d’existence.
Comparatif technique : performance, flexibilité, maintenance
Prenons un exemple concret : afficher une liste de 10 articles avec titre, extrait et image mise en avant.
En REST API, l’appel /wp-json/wp/v2/posts?_embed renvoie le contenu complet en HTML (souvent plusieurs Ko par article), les métadonnées, les liens _links et les ressources embarquées. Pour extraire juste ce dont on a besoin, il faut parser côté frontend.
En GraphQL, la requête ressemble à ça :
query {
posts {
nodes {
title
excerpt
featuredImage {
node { sourceUrl }
}
}
}
}
Le payload est minimal, ciblé, et ne contient que ce qu’on a demandé. Sur un site avec beaucoup de contenu ou un trafic important, cette différence se ressent directement sur les temps de build et de chargement.
Côté maintenance, REST API reste plus simple à déboguer (format JSON standard, pas de schema à maintenir). GraphQL demande un peu plus de rigueur, mais offre une meilleure évolutivité quand le projet grossit.
Notre recommandation selon le type de projet
Alors, lequel choisir ? Ça dépend clairement de la complexité du projet.
Pour un blog simple, une vitrine ou un petit site sans relations complexes entre les contenus, la REST API native suffit largement. Pas besoin d’installer un plugin supplémentaire, pas de courbe d’apprentissage : on va droit au but.
Par contre, dès qu’on a des champs ACF à exposer, des relations entre contenus (articles liés, taxonomies personnalisées, custom post types imbriqués) ou des besoins de performance sur le nombre de requêtes, WPGraphQL devient rapidement indispensable. C’est un investissement initial (installation, apprentissage des queries), mais qui paie sur la durée, surtout si le site est amené à grossir.
Mon conseil : commencez avec REST API si vous débutez sur Astro + WordPress headless. Passez à WPGraphQL dès que vous sentez les limites (multiples appels, payloads trop lourds, champs ACF à gérer).
Exemple concret : récupérer du contenu WordPress dans Astro
Assez de théorie, passons à la pratique. Voici un exemple concret et fonctionnel pour récupérer les articles WordPress et les afficher dans une page Astro. On reste volontairement simple : l’idée, c’est de comprendre le mécanisme, pas de construire une usine à gaz.
Configuration de la connexion à l’API
Première règle : on ne code jamais l’URL de l’API en dur dans le fichier .astro. On passe par une variable d’environnement, histoire de pouvoir changer facilement de backend (dev, staging, prod) sans toucher au code.
Dans un fichier .env à la racine du projet :
WP_API_URL=https://monsite.fr/wp-json/wp/v2
Et dans astro.config.mjs, rien de spécial à faire : Astro lit automatiquement le .env et expose les variables via import.meta.env (à condition qu’elles soient préfixées correctement ou utilisées côté serveur, ce qui est le cas ici puisqu’on fetch au build).
Si vous utilisez WPGraphQL plutôt que la REST API, la logique est identique, seule l’URL change (généralement /graphql en suffixe) et la requête devient une query GraphQL au lieu d’un simple GET. On y reviendra brièvement plus bas.
Récupération et affichage des articles en Astro
Voici le fichier src/pages/blog.astro complet, avec le fetch dans le frontmatter et l’affichage dans le template :
---
// Typage optionnel mais recommandé avec TypeScript
interface WPPost {
id: number;
title: { rendered: string };
excerpt: { rendered: string };
slug: string;
}
let posts: WPPost[] = [];
try {
const res = await fetch(`${import.meta.env.WP_API_URL}/posts?_embed`);
if (!res.ok) throw new Error(`Erreur API : ${res.status}`);
posts = await res.json();
} catch (error) {
console.error("Impossible de récupérer les articles WordPress :", error);
// Fallback : tableau vide pour éviter que le build casse
posts = [];
}
---
<html lang="fr">
<body>
<h1>Blog</h1>
{posts.length === 0 && <p>Aucun article disponible pour le moment.</p>}
<ul>
{posts.map((post) => (
<li>
<h2 set:html={post.title.rendered} />
<p set:html={post.excerpt.rendered} />
</li>
))}
</ul>
</body>
</html>
Quelques précisions importantes :
- Le
try/catchn’est pas optionnel. Si l’API WordPress est down au moment du build (serveur mutualisé capricieux, maintenance, timeout réseau), tout votre site statique refuse de se générer sans ça. Autant dire que c’est le genre de bug qu’on découvre en prod, à 18h un vendredi. - Le typage TypeScript (
WPPost) sécurise l’accès aux propriétés. Ce n’est pas obligatoire, mais franchement, ça évite pas mal deundefined is not a functionquand l’API renvoie un format légèrement différent de ce qu’on attendait. set:htmlest utilisé volontairement ici car WordPress renvoie du HTML déjà formaté dansrendered(balises<p>,<strong>, etc.). Sans ça, vous afficheriez le code HTML brut à l’écran.
Pour la version GraphQL, le principe reste identique, seule la requête change :
const query = `
query {
posts {
nodes { title excerpt slug }
}
}
`;
const res = await fetch(`${import.meta.env.WP_API_URL}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query }),
});
const { data } = await res.json();
const posts = data.posts.nodes;
Et voilà : avec ce squelette, vous avez de quoi récupérer et afficher n’importe quel contenu WordPress dans Astro. Reste à adapter selon vos champs ACF, votre pagination, ou vos besoins de cache (mais ça, c’est une autre histoire).
Les pièges fréquents du headless WordPress à anticiper
Passer en headless, c’est génial sur le papier. Mais en pratique, on se cogne assez vite à des fonctionnalités WordPress qu’on considérait comme acquises et qui, du jour au lendemain, ne fonctionnent plus du tout. Rien de rédhibitoire, rassurez-vous : mais il faut le savoir avant de se lancer, histoire de ne pas découvrir ça en pleine mise en prod (vécu, malheureusement).
L’aperçu des brouillons cassé par défaut
Premier réflexe cassé : le bouton « Aperçu » de l’admin WordPress. Par défaut, il pointe vers l’URL du thème classique, celui que vous n’utilisez plus. Résultat : clic sur Aperçu, et… rien. Ou pire, une page blanche qui ne présage rien de bon pour votre rédacteur.
La solution consiste à recoder un système de preview côté Astro. Deux approches possibles ici : soit une route dédiée avec un token de sécurité (genre /preview/[slug]?token=xxx), soit une requête directe à l’API pour récupérer le statut « draft » avec authentification. Dans les deux cas, comptez du temps de dev supplémentaire, ce n’est pas du out-of-the-box.
La gestion des redirections qui échappe à WordPress
Plugins comme Redirection ou Yoast SEO gèrent habituellement les redirections via .htaccess ou en PHP, côté serveur WordPress. Sauf qu’en headless, ce backend n’est plus jamais appelé directement par le visiteur : il ne sert que d’API. Donc ces règles de redirection deviennent invisibles pour le frontend Astro.
Concrètement, il faut dupliquer cette logique ailleurs. Deux options courantes : un fichier de redirections géré directement par l’hébergeur ou un middleware Astro, ou alors exposer les règles WordPress via une API custom que le frontend consulte au build ou à la volée. Aucune des deux n’est instantanée à mettre en place, et c’est souvent le piège numéro un qu’on oublie de budgétiser.
Les formulaires ACF et plugins qui ne fonctionnent plus nativement
Les champs ACF, en lecture seule via l’API, ça se passe très bien. Le vrai souci arrive avec les formulaires interactifs : Gravity Forms, Contact Form 7, et compagnie. Ces plugins génèrent normalement du HTML et du JS directement dans le thème WordPress. Or, en headless, ce rendu n’existe simplement plus côté frontend.
Il faut donc recoder le formulaire à la main côté Astro, avec un endpoint WordPress custom pour recevoir les données en POST. Autre option, plus rapide à mettre en œuvre mais avec un coût récurrent : passer par un service tiers (Formspree, par exemple). Dans tous les cas, ce n’est plus du « installer et ça marche ».
Le cache et la revalidation du contenu
Dernier piège, souvent sous-estimé : comment savoir qu’un contenu WordPress vient de changer pour redéclencher un build Astro ? Sans ça, votre site frontend affiche du contenu périmé, parfois pendant des heures.
Plusieurs pistes existent : les webhooks WordPress qui déclenchent un rebuild à chaque publication, l’ISR (Incremental Static Regeneration) si Astro tourne en mode hybride/serveur, ou tout simplement un rebuild complet planifié. Chaque solution a ses compromis entre fraîcheur du contenu et coût en ressources.
Bref, ces quatre pièges ne remettent pas en cause l’architecture headless en elle-même. Mais ils demandent clairement du temps de développement additionnel, à chiffrer sérieusement avant de vendre le projet ou de s’engager dessus.
