Bonnes pratiques
Architecture, cache, performance : les réflexes d'un pro du headless.
9.1Architecture de fichiers recommandée
projet/
├── frontend/ # Next.js
│ ├── app/ # Pages
│ ├── components/ # Composants React
│ ├── lib/
│ │ ├── wordpress.ts # Fonctions fetch WP
│ │ ├── types/
│ │ │ └── wordpress.ts # Types des réponses API
│ │ └── utils/
│ │ └── sanitize.ts # Nettoyage HTML WordPress
│ ├── .env.local # WP_API_URL (secret)
│ └── next.config.ts # Rewrites proxy dev
│
├── backend/ # Plugin WordPress
│ ├── loader.php # Point d'entrée
│ └── modules/
│ ├── cors-security.php # Config CORS
│ ├── auth-endpoint.php # Login/register/me
│ └── projets-endpoint.php # Endpoints métier
│
└── docs/ # Documentation
├── ENDPOINTS.md # Liste des endpoints
└── GOLDEN_RULES.md # Règles CORS/API/Auth9.2Stratégie de cache
Le cache est ton meilleur ami en headless. Voici les 3 niveaux :
Niveau 1 : Next.js ISR
Revalide les pages en arrière-plan. Le visiteur voit toujours une page statique ultra-rapide.
// Revalide toutes les 60 secondes
fetch(url, { next: { revalidate: 60 } })Niveau 2 : CDN edge cache
Cloudflare/Vercel cache les pages HTML au plus proche du visiteur. Latence minimale.
# Headers Cache-Control dans la réponse
Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400Niveau 3 : Cache WordPress
Object cache (Redis/Memcached) côté WordPress pour accélérer les requêtes PHP.
// Utilise le transient API pour cacher les résultats
$data = get_transient('projets_liste');
if (false === $data) {
$data = fetch_projets_from_db();
set_transient('projets_liste', $data, 300); // 5 min
}9.3Gestion des images
Les images WordPress sont sur le domaine du CMS. Tu ne veux pas les servir depuis cms.monsite.fr directement. Deux approches :
Option A : Next.js Image
Configure les domaines autorisés dans next.config.ts et utilise le composant Image.
// next.config.ts
images: {
remotePatterns: [{
protocol: 'https',
hostname: 'cms.monsite.fr',
}],
}Option B : CDN dédié
Proxy les images via Cloudflare Images ou imgix pour ne pas exposer le domaine CMS.
// Remap les URLs d'images
function proxyImageUrl(wpUrl: string) {
return wpUrl.replace(
'https://cms.monsite.fr',
'https://img.monsite.fr'
)
}9.4Sanitiser le HTML WordPress
Le contenu WordPress contient du HTML brut. Avant de l'injecter dans tes composants React, nettoie-le :
// lib/utils/sanitize.ts
import DOMPurify from 'isomorphic-dompurify'
export function sanitizeWPContent(html: string): string {
return DOMPurify.sanitize(html, {
ALLOWED_TAGS: [
'p', 'br', 'strong', 'em', 'a', 'ul', 'ol', 'li',
'h2', 'h3', 'h4', 'blockquote', 'code', 'pre',
'img', 'figure', 'figcaption',
],
ALLOWED_ATTR: [
'href', 'target', 'rel', 'src', 'alt', 'class',
'width', 'height', 'loading',
],
})
}
// Utilisation dans un composant
<div
dangerouslySetInnerHTML={{
__html: sanitizeWPContent(post.content.rendered)
}}
/>9.5Déploiement
En production, le déploiement typique :
| Composant | Hébergement | Pourquoi |
|---|---|---|
| Next.js | Cloudflare Pages / Vercel | CDN global, edge functions, HTTPS auto |
| WordPress | o2switch / Infomaniak / VPS | PHP + MySQL, SSL, backups |
| Proxy API | Cloudflare Functions / Vercel | Même plateforme que le front |
| Images | WordPress + CDN | Cloudflare cache les uploads |
9.6Les règles d'or
/api/wp-json/..., jamais cms.monsite.fr./login pas /login/. La redirection casse les cookies.any. Chaque endpoint a son type TypeScript.ENDPOINTS.md dans docs/ avec chaque route, ses params et sa réponse.