BaliseTonSite

Proxy API et sécurité

Ton WordPress ne doit jamais être accessible directement. Jamais.

8.1Pourquoi un proxy ?

Sans proxy, ton front appelle directement WordPress :

Bashbash
# MAUVAIS : le navigateur appelle directement WordPress
Browser https://cms.monsite.fr/wp-json/wp/v2/posts

          L'URL WordPress est exposée dans le DevTools
          Navigateur, attaques ciblées, robots...

Avec un proxy, tout passe par ton domaine :

Bashbash
# BON : le navigateur appelle ton propre domaine
Browser https://www.monsite.fr/api/wp-json/wp/v2/posts

              Proxy (Cloudflare/Next.js)

          https://cms.monsite.fr/wp-json/wp/v2/posts

          URL invisible pour le visiteur

Les avantages :

  • Sécurité : l'URL WordPress n'apparaît nulle part côté client.
  • Pas de CORS : le navigateur appelle le même domaine, donc pas de cross-origin.
  • Cache : tu peux cacher les réponses au niveau du proxy (CDN edge).
  • Contrôle : tu peux filtrer, limiter ou transformer les requêtes avant qu'elles atteignent WordPress.

8.2Proxy avec Next.js rewrites (développement)

En développement local, Next.js peut jouer le rôle de proxy grâce aux rewrites :

TypeScripttypescript
// next.config.ts

import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  async rewrites() {
    return [
      {
        source: '/api/wp-json/:path*',
        destination: 'http://mon-cms-headless.local/wp-json/:path*',
      },
    ]
  },
}

export default nextConfig

Maintenant, depuis ton code Next.js, tu appelles /api/wp-json/... et Next.js reroute vers WordPress de manière transparente.

8.3Proxy avec Cloudflare Pages Functions (production)

En production, les rewrites de Next.js ne suffisent pas si tu héberges sur Cloudflare Pages. Il te faut une Pages Function qui fait le proxy :

TypeScripttypescript
// functions/api/[[path]].ts
// Catch-all : intercepte toutes les requêtes /api/*

interface Env {
  WP_API_ORIGIN: string  // https://cms.monsite.fr
}

export const onRequest: PagesFunction<Env> = async (context) => {
  const url = new URL(context.request.url)

  // Reconstruit l'URL WordPress
  const wpPath = url.pathname.replace(/^\/api/, '')
  const wpUrl = `${context.env.WP_API_ORIGIN}${wpPath}${url.search}`

  // Transmet la requête avec les cookies
  const wpResponse = await fetch(wpUrl, {
    method: context.request.method,
    headers: context.request.headers,
    body: context.request.method !== 'GET'
      ? await context.request.text()
      : undefined,
  })

  // Renvoie la réponse au navigateur
  const response = new Response(wpResponse.body, {
    status: wpResponse.status,
    headers: wpResponse.headers,
  })

  return response
}

8.4Protéger WordPress côté serveur

Le proxy cache l'URL, mais si quelqu'un la découvre, WordPress est toujours accessible. Ajoute une couche de protection côté serveur :

Bashbash
# .htaccess (Apache / o2switch)

# Bloque l'accès direct à wp-json sauf depuis le proxy
<IfModule mod_rewrite.c>
  RewriteEngine On

  # Autorise les requêtes depuis le proxy Cloudflare
  # et les requêtes internes (wp-admin)
  RewriteCond %{HTTP:X-Forwarded-For} !^(104\.16\.|172\.67\.)
  RewriteCond %{REQUEST_URI} ^/wp-json/
  RewriteCond %{HTTP_REFERER} !^https://cms\.monsite\.fr
  RewriteRule ^ - [F,L]
</IfModule>

En complément, limite l'accès à wp-admin par IP ou par authentification HTTP :

PHPphp
<?php
// Dans un plugin : restreindre l'API REST aux origines autorisées
add_filter('rest_authentication_errors', function($result) {
    if (!empty($result)) {
        return $result;
    }

    $origin = $_SERVER['HTTP_ORIGIN'] ?? '';
    $referer = $_SERVER['HTTP_REFERER'] ?? '';
    $allowed = [
        'https://www.monsite.fr',
        'https://cms.monsite.fr',
    ];

    // En production, vérifie l'origine
    $is_allowed = false;
    foreach ($allowed as $domain) {
        if (str_starts_with($origin, $domain) ||
            str_starts_with($referer, $domain)) {
            $is_allowed = true;
            break;
        }
    }

    // Autorise toujours les requêtes internes (cron, CLI)
    if (defined('REST_REQUEST') && !$is_allowed && php_sapi_name() !== 'cli') {
        return new WP_Error(
            'rest_forbidden',
            'Accès non autorisé.',
            ['status' => 403]
        );
    }

    return $result;
});

8.5Checklist sécurité headless

L'URL WordPress n'est jamais dans le code front-end (pas de NEXT_PUBLIC_)
Toutes les requêtes passent par le proxy (/api/wp-json/...)
CORS ne permet que les origines autorisées (pas de *)
Les cookies sont HttpOnly, Secure, SameSite=None
WordPress est en HTTPS avec un certificat valide
Les endpoints sensibles utilisent permission_callback

Verifie tes acquis

5 questions pour valider ce chapitre

1. Quel est le role principal du proxy API dans une architecture headless ?

Valide et sauvegarde ce chapitre

Ne perds pas le fil de ton apprentissage. Chaque QCM terminé sauvegarde ton score. Crée ton profil gratuitement pour débloquer toutes les évaluations du site et retrouver tes résultats plus tard.

Commencer l'aventure
Déjà membre ?Connecte-toi