# Sécuriser les Server Components qui appellent la base WordPress sans l’API REST

> Contourner l'API REST pour interroger $wpdb directement depuis un composant serveur pose des questions d'authentification et de couplage inédites. Ce qu'il faut verrouiller.

- Auteur : Clément Hadrot
- Publié le : 2025-09-15
- Mis à jour le : 2025-09-15
- Catégorie : Headless &amp; API
- URL : https://wpmoderne.dev.wordpress-developpement.fr/headless/securiser-server-components-appellent-wpdb-sans-api-rest/

## L’essentiel

- Interroger $wpdb directement supprime toute la couche de validation et de permission de l'API REST
- Un composant serveur qui accède à la base doit recevoir des identifiants distincts, jamais ceux d'un compte administrateur
- Ce couplage direct rend toute évolution du schéma de base de données un risque de rupture silencieuse côté front

L'idée est apparue lors d'un atelier d'architecture pour un site média à très fort trafic : et si, pour gagner en performance sur les pages les plus consultées, un composant serveur Next.js interrogeait directement la base de données MySQL de WordPress, en contournant totalement l'API REST et son coût de sérialisation JSON intermédiaire ? Techniquement faisable, puisqu'un composant serveur s'exécute dans un environnement Node capable d'ouvrir une connexion MySQL classique. La question qui s'est immédiatement posée était : à quel prix en matière de sécurité et de maintenabilité ?

Ce texte documente l'analyse menée avant de trancher, et les garde-fous mis en place sur le prototype qui a finalement été testé, avant d'être volontairement écarté pour la majorité des cas d'usage du projet.

## Ce que l'API REST fait, et qu'on oublie qu'elle fait

L'API REST WordPress n'est pas qu'un simple traducteur de requêtes SQL en JSON. Elle applique, à chaque appel, une chaîne de vérifications qui disparaît intégralement si l'on interroge `$wpdb` directement : vérification des permissions via `permission_callback`, filtrage des champs privés selon le rôle de l'utilisateur authentifié, application des hooks `the_content` qui transforment le contenu brut stocké en base (shortcodes, oEmbed, filtres de sécurité), et validation des paramètres de requête pour éviter les injections.

Un composant serveur qui exécute directement `SELECT post_content FROM wp_posts WHERE post_status = 'publish'` récupère un contenu brut, potentiellement porteur de shortcodes non traités, de références à des révisions, ou de statuts de publication mal filtrés si la requête SQL contient la moindre erreur d'écriture. Aucune de ces protections n'est acquise automatiquement.

## Le prototype testé et ses garde-fous

> L'essentiel à retenir : Interroger $wpdb directement supprime toute la couche de validation et de permission de l'API REST ; Un composant serveur qui accède à la base doit recevoir des identifiants distincts, jamais ceux d'un compte administrateur ; Ce couplage direct rend toute évolution du schéma de base de données un risque de rupture silencieuse côté front

Le cas d'usage retenu pour le test était volontairement restreint : une page d'accueil affichant les douze derniers titres publiés, sans contenu complet, un besoin où la performance de lecture prime largement sur la richesse de la donnée. Plusieurs garde-fous ont été posés avant même d'écrire la requête :

- Création d'un compte MySQL dédié en lecture seule, sans aucun droit d'écriture, distinct du compte utilisé par WordPress lui-même pour son propre fonctionnement.
- Restriction de ce compte à une seule table via les privilèges MySQL natifs, en l'occurrence uniquement `wp_posts`, sans accès à `wp_users` ni `wp_options`, qui contiennent des données bien plus sensibles.
- Connexion établie exclusivement depuis l'environnement serveur du front, jamais exposée à un composant client, avec les identifiants stockés en variable d'environnement et jamais commis dans le dépôt de code.

```
-- Création du compte restreint, exécutée une seule fois
CREATE USER 'lecture_titres'@'%' IDENTIFIED BY '***';
GRANT SELECT (ID, post_title, post_status, post_date_gmt)
  ON wordpress.wp_posts TO 'lecture_titres'@'%';
```

```
async function DerniersTitres() {
  const [rows] = await pool.query(
    `SELECT post_title, post_date_gmt FROM wp_posts
     WHERE post_status = 'publish' AND post_type = 'post'
     ORDER BY post_date_gmt DESC LIMIT 12`
  );
  return (
    <ul>
      {rows.map((r) => <li key={r.post_title}>{r.post_title}</li>)}
    </ul>
  );
}
```

Même avec la restriction de colonnes au niveau MySQL, la requête reconstruit manuellement la logique de filtrage par statut que l'API REST applique nativement, avec un risque réel d'oubli si un futur statut personnalisé (comme un statut de contenu programmé propre au site) venait à être ajouté sans que cette requête directe ne soit mise à jour en conséquence.

## Le vrai risque : le couplage au schéma interne

Le danger le plus sérieux identifié n'était pas immédiatement la sécurité d'accès, correctement traitée par les garde-fous ci-dessus, mais le couplage structurel que cette approche crée entre le front et le schéma de base de données interne de WordPress. Ce schéma n'est documenté par aucune garantie de stabilité officielle contrairement à l'API REST, dont la structure de réponse est un contrat public. Une mise à jour de WordPress modifiant la structure d'une table, ou un plugin ajoutant une colonne qui entre en conflit, romprait silencieusement cette requête directe, sans aucun message d'erreur explicite côté API puisqu'il n'y a plus d'API pour signaler quoi que ce soit.

### La décision finale de l'équipe

Ce couplage a été jugé disproportionné par rapport au gain de performance mesuré, environ 40 millisecondes gagnées par rapport à un appel API REST classique correctement mis en cache. Le prototype a été abandonné pour la production, au profit d'un cache applicatif plus agressif sur l'endpoint REST existant, une solution moins risquée pour un gain de performance équivalent.

> Contourner une couche d'abstraction pour gagner quelques millisecondes n'a de sens que si le gain dépasse largement le risque de couplage créé ; ici, ce n'était clairement pas le cas.

## En résumé

Interroger directement `$wpdb` depuis un composant serveur reste techniquement possible et, dans de rares cas très spécifiques, justifiable avec des garde-fous stricts de permissions restreintes. Mais le risque principal n'est pas seulement une faille de sécurité immédiate évitable par une bonne configuration : c'est un couplage silencieux à un schéma de données non contractuel, qui expose le projet à des ruptures difficiles à diagnostiquer bien après que la décision initiale a été oubliée par l'équipe.
