Erreur de l’API REST WordPress : ce qui casse REST et comment dépanner

Cet article vous a aidé ?

Certaines pannes WordPress sont bruyantes : écran blanc, erreurs fatales, le genre de chose que vous remarquez avant que votre café ne refroidisse. Les pannes de l’API REST sont plus discrètes et plus agaçantes. Votre éditeur n’enregistre plus, votre frontend headless devient vide, WooCommerce passe à la caisse « parfois », et le seul indice est un message blasé du type « The REST API encountered an error. »

Voici la réalité pratique : l’API REST n’est pas une seule fonctionnalité. C’est une chaîne — réécritures, routage, authentification, cookies/nonces, couches de cache, middleware de sécurité et votre pile d’hébergement — qui peuvent chacune casser indépendamment. Nous allons isoler quel maillon a lâché, avec des commandes que vous pouvez lancer, des sorties que vous pouvez interpréter et des décisions à prendre sans deviner.

Comment l’API REST de WordPress fonctionne réellement (et où elle échoue)

Quand quelqu’un dit « l’API REST de WordPress est en panne », il veut généralement dire une de ces choses :

  • La route est inaccessible (réécritures, permaliens, config serveur, proxy inverse, CDN ou WAF en sont la cause).
  • La route est atteinte mais bloquée (échecs d’authentification, problèmes de nonce/cookie, contrôles de capacités, plugins de sécurité ou règles WAF).
  • La route s’exécute puis plante (erreur PHP fatale, timeout base de données, limites mémoire, chemin de code plugin, erreurs de sérialisation).
  • La route s’exécute mais la réponse est corrompue (mise en cache, compression, changement de content-type, redirections inattendues, contenu mixte, CORS).

Sur un site sain, le point de terminaison canonique est :

  • /wp-json/ (un index JSON des namespaces et des routes)
  • Exemple de route : /wp-json/wp/v2/posts

Sous le capot, WordPress utilise des règles de réécriture pour router /wp-json/... vers index.php, où le serveur REST dispatch les routes enregistrées. Cela signifie que même les pannes « API » commencent souvent par de banales défaillances de réécriture.

Aussi : les requêtes REST ne sont que des requêtes HTTP. C’est leur force et leur faiblesse. Chaque middlebox qui a « aidé » un site (cache, CDN, firewall, bloqueur de bots, filtre gzip, minifier, « durcisseur de sécurité ») peut envoyer votre API REST au cimetière.

Une citation opérationnelle pour rester lucide :

« L’espoir n’est pas une stratégie. » — Gen. Gordon R. Sullivan

Où les défaillances se concentrent

En production, les problèmes d’API REST se regroupent autour de cinq couches :

  1. DNS/TLS/edge : certificats, incompatibilités SNI, redirections HTTP→HTTPS, cache CDN, blocages WAF.
  2. Proxy inverse / serveur web : réécritures Nginx/Apache, normalisation de chemin, suppression d’en-têtes, limites de taille de corps de requête.
  3. Couche application WordPress : réglages des permaliens, enregistrement des routes, conflits de plugins, logique d’auth et de nonce.
  4. État et stockage : verrous/timeouts DB, comportements étranges du cache d’objets, permissions du système de fichiers.
  5. Comportement client : frontend JS, éditeur Gutenberg, CORS, cookies mixtes, nonces obsolètes.

Ne dépannez pas depuis l’interface d’administration WordPress en premier. C’est comme diagnostiquer une panne réseau en regardant le fond d’écran de votre laptop. Commencez par faire une requête depuis l’extérieur du navigateur avec des entrées et sorties connues.

Guide rapide de diagnostic

Quand le REST est « en panne », vous voulez une réponse en quelques minutes : est-ce l’edge/proxy, l’app ou le backend ? Suivez ce playbook dans l’ordre. Ne sautez pas d’étapes parce que vous « savez déjà ». C’est ainsi que les incidents deviennent des mémoires.

Première étape : confirmer le point de terminaison et la vérité HTTP

  1. Demandez /wp-json/ avec curl et enregistrez : code de statut, content-type et éventuelles redirections.
  2. Comparez depuis deux points de vue : depuis votre laptop et depuis le serveur (ou une machine du même réseau que WordPress).
  3. Vérifiez la présence d’HTML au lieu de JSON. L’HTML signifie souvent que vous récupérez une page de connexion, une page de blocage WAF ou un template d’erreur mis en cache.

Deuxième étape : isoler l’edge/la sécurité de WordPress

  1. Contournez le CDN si possible (hôte d’origine direct, VIP du load balancer interne ou override de fichier hosts dans un environnement contrôlé).
  2. Vérifiez les logs WAF/plugins de sécurité pour des blocs sur /wp-json et pour des signatures courantes (faux positifs SQLi/XSS).
  3. Confirmez l’absence de boucles de redirection forcées (HTTP→HTTPS→HTTP, www→non-www, règles de slash final).

Troisième étape : prouver le routage WordPress et les permaliens

  1. Vérifiez que les permaliens ne sont pas réglés sur « Plain ». Le REST peut fonctionner en mode plain, mais les réécritures deviennent souvent incohérentes quand on bascule les réglages sans vider les règles.
  2. Confirmez que les réécritures existent dans la config serveur (règles Nginx location) ou dans .htaccess (Apache).
  3. Vérifiez si index.php?rest_route=/ fonctionne. Si oui, les réécritures sont cassées ; si non, c’est l’app ou un blocage.

Quatrième étape : authentifiez seulement après que le chemin fonctionne

  1. Testez d’abord les endpoints publics (liste de posts) avant les endpoints privés (utilisateurs, réglages).
  2. Si l’auth est nécessaire, testez avec Application Passwords ou Basic Auth (dans un environnement sûr) pour éliminer la complexité nonce/cookie.
  3. Puis testez les flux cookie+nonce pour les problèmes d’éditeur.

Cinquième étape : si c’est lent ou que ça renvoie 500, traitez comme saturation backend

  1. Corrélez avec la saturation PHP-FPM, les requêtes lentes DB et les logs d’erreur.
  2. Prélevez un échantillon de requêtes en échec et tracez : log d’accès du serveur web → log d’erreur PHP → log debug WP → logs DB.
  3. Décidez : rollback d’un plugin/thème, montée en capacité, ou atténuation avec des exemptions de cache.

Règle de décision : Si /wp-json/ n’est pas un 200 JSON stable depuis deux points de vue, ne perdez pas de temps à déboguer les fonctionnalités WordPress. Corrigez le routage/edge d’abord.

Carte symptôme→couche : décodez les erreurs par comportement HTTP

Les erreurs REST apparaissent souvent comme une notification générique dans l’admin WordPress. Ignorez la notification et regardez le HTTP.

Codes de statut et leur signification habituelle

  • 200 mais mauvais content-type (HTML) : vous tombez sur une page de connexion, une page de blocage WAF, un template d’erreur mis en cache ou une cible de redirection.
  • Boucles 301/302 : règles de canonicalisation qui se battent (HTTP/HTTPS, www/non-www, slash final), ou en-têtes proxy mal configurés.
  • 401 Unauthorized : identifiants manquants/invalides, échec de nonce, Application Passwords désactivés, plugin de sécurité qui intercepte.
  • 403 Forbidden : règle WAF/CDN, mod_security, restrictions IP, « blocage des requêtes JSON », ou échec d’un contrôle de capacité.
  • 404 Not Found : réécritures non fonctionnelles, mismatch de location Nginx, problèmes multisite, ou routes non enregistrées.
  • 405 Method Not Allowed : proxy/serveur qui interdit POST/PUT, ou couche de cache qui n’autorise que GET.
  • 413 Payload Too Large : limites de taille côté proxy ; uploads REST échouent, endpoints médias échouent.
  • 429 Too Many Requests : limiteur de débit, WAF, protection bots, ou plugin de throttling agressif.
  • 500/502/503 : fatal PHP, crash en amont, php-fpm épuisé, timeouts DB, origine indisponible ou timeouts de gateway.

Blague #1 : L’API REST est « sans état », ce qui est adorable — comme appeler un tout-petit « calme » cinq minutes avant que le sucre frappe.

Tâches pratiques (commandes, sorties, décisions)

Ce ne sont pas des « astuces ». Ce sont des tâches reproductibles. Chacune inclut une commande, ce que la sortie signifie et la décision suivante.

Task 1: Check the REST index from your workstation

cr0x@server:~$ curl -sS -D- -o /tmp/rest.json https://example.com/wp-json/ | head -n 20
HTTP/2 200
content-type: application/json; charset=UTF-8
x-robots-tag: noindex
x-content-type-options: nosniff

Ce que signifie la sortie : Vous avez obtenu un 200 et un content-type JSON. Bien : le routage et le chemin de réponse basique fonctionnent depuis l’extérieur.

Décision : Passez aux vérifications liées à l’auth et aux plugins. Si vous voyez 301/302, suivez les redirections (-L) et inspectez la canonicalisation. Si le content-type est text/html, vous n’obtenez pas l’API REST.

Task 2: Verify whether you’re getting HTML instead of JSON

cr0x@server:~$ curl -sS -D- https://example.com/wp-json/ | sed -n '1,25p'
HTTP/2 403
content-type: text/html; charset=UTF-8
server: cloudflare

<!doctype html>
<html>
<head><title>Access denied</title></head>

Ce que signifie la sortie : 403 avec une page HTML de blocage et un en-tête CDN/serveur. Ce n’est pas WordPress. C’est l’edge sécurité.

Décision : Arrêtez de toucher WordPress. Examinez les règles WAF/CDN/firewall et contournez l’edge pour vérifier la santé de l’origine.

Task 3: Test from the origin host to bypass CDN/WAF (where possible)

cr0x@server:~$ curl -sS -D- -H 'Host: example.com' http://127.0.0.1/wp-json/ | head -n 15
HTTP/1.1 200 OK
Content-Type: application/json; charset=UTF-8
X-Powered-By: PHP/8.2.12

Ce que signifie la sortie : L’origine sert bien du JSON. L’edge est le problème.

Décision : Corrigez la politique edge : autorisez /wp-json/*, autorisez les méthodes requises, ajustez les règles bot, ou ajoutez une exception WAF pour les endpoints REST (fortement ciblée).

Task 4: Determine if rewrites are broken by using rest_route

cr0x@server:~$ curl -sS -D- "https://example.com/index.php?rest_route=/" | head -n 15
HTTP/2 200
content-type: application/json; charset=UTF-8

Ce que signifie la sortie : L’API REST fonctionne lorsque vous contournez les réécritures. Donc votre couche de réécriture est cassée.

Décision : Corrigez la config de réécriture du serveur ou videz les permaliens. Ne perdez pas de temps à déboguer les plugins tant que /wp-json/ ne fonctionne pas comme index.php?rest_route=/.

Task 5: Check Apache rewrite and .htaccess health

cr0x@server:~$ sudo apachectl -M | egrep 'rewrite|headers'
 rewrite_module (shared)
 headers_module (shared)

Ce que signifie la sortie : Les modules requis existent. Si rewrite est absent, les permaliens et les routes REST échouent souvent.

Décision : Si rewrite_module est absent, activez-le et rechargez Apache. Si présent, inspectez ensuite le vhost et les règles de .htaccess.

Task 6: Validate Nginx routing for wp-json (common pitfall)

cr0x@server:~$ sudo nginx -T 2>/dev/null | sed -n '/server_name example.com/,/}/p' | egrep -n 'location|try_files|rewrite'
12:    location / {
13:        try_files $uri $uri/ /index.php?$args;
14:    }
28:    location ~ \.php$ {
29:        include snippets/fastcgi-php.conf;
30:        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
31:    }

Ce que signifie la sortie : Un fallback try_files sain redirige les chemins inconnus (y compris /wp-json/) vers index.php.

Décision : Si vous voyez try_files $uri =404; sans fallback vers index, corrigez cela ou REST renverra 404. Après modification, reload Nginx et retestez.

Task 7: Confirm WordPress thinks the site URL matches reality

cr0x@server:~$ wp option get siteurl --path=/var/www/html
https://example.com
cr0x@server:~$ wp option get home --path=/var/www/html
https://example.com

Ce que signifie la sortie : Ces URLs pilotent les redirections canoniques et les cookies. Un mismatch (http vs https, www vs non-www) casse l’auth via nonce/cookie et crée des boucles de redirection.

Décision : Si elles sont incorrectes, corrigez-les (avec précaution) puis validez les redirections et les appels REST.

Task 8: Inspect WordPress rewrite rules and flush them safely

cr0x@server:~$ wp rewrite list --path=/var/www/html | head -n 12
^wp-json/?$		index.php?rest_route=/	last
^wp-json/(.+)?$	index.php?rest_route=/$matches[1]	last
^wp-sitemap\.xml$	index.php?sitemap=index	last

Ce que signifie la sortie : Les règles de réécriture REST existent au niveau WordPress.

Décision : Si celles-ci manquent, videz les règles :

cr0x@server:~$ wp rewrite flush --hard --path=/var/www/html
Success: Rewrite rules flushed.

Décision : Si WordPress a les règles mais que HTTP renvoie toujours 404, votre serveur web ne les prend pas en compte (config Nginx, Apache AllowOverride, etc.).

Task 9: Check whether Apache allows .htaccess overrides

cr0x@server:~$ sudo apachectl -S 2>/dev/null | sed -n '1,80p'
VirtualHost configuration:
*:80                   example.com (/etc/apache2/sites-enabled/example.conf:1)

cr0x@server:~$ sudo sed -n '1,120p' /etc/apache2/sites-enabled/example.conf | egrep -n 'DocumentRoot|Directory|AllowOverride'
2: DocumentRoot /var/www/html
5: <Directory /var/www/html>
6:     AllowOverride None
7: </Directory>

Ce que signifie la sortie : AllowOverride None signifie que votre .htaccess est ignoré. Les permaliens et les réécritures REST échouent à moins que le vhost n’ait des règles équivalentes.

Décision : Mettez AllowOverride All (ou ajoutez explicitement les règles de réécriture dans le vhost), rechargez Apache, retestez /wp-json/.

Task 10: Identify if a plugin is intercepting REST requests

cr0x@server:~$ wp plugin list --status=active --path=/var/www/html
+-------------------------+--------+-----------+---------+
| name                    | status | update    | version |
+-------------------------+--------+-----------+---------+
| wordfence               | active | none      | 7.11.6  |
| w3-total-cache          | active | available | 2.7.5   |
| classic-editor          | active | none      | 1.6.7   |
+-------------------------+--------+-----------+---------+

Ce que signifie la sortie : Les plugins de sécurité et de cache sont des causes fréquentes de problèmes REST. Ce n’est pas de la culpabilité ; c’est de la probabilité.

Décision : Désactivez temporairement les plugins suspects (dans une fenêtre contrôlée) et retestez. Commencez par les plugins WAF/sécurité et les caches agressifs.

Task 11: Temporarily disable plugins without killing production blindly

cr0x@server:~$ wp plugin deactivate wordfence w3-total-cache --path=/var/www/html
Plugin 'wordfence' deactivated.
Plugin 'w3-total-cache' deactivated.

Ce que signifie la sortie : Vous testez si le REST revient avec l’interception supprimée.

Décision : Si REST revient, réactivez un plugin à la fois pour isoler. Puis configurez : autorisez /wp-json/, désactivez le blocage REST et excluez le cache pour les endpoints REST.

Task 12: Check for REST-related errors in PHP logs

cr0x@server:~$ sudo tail -n 40 /var/log/php8.2-fpm.log
[27-Dec-2025 10:12:41] WARNING: [pool www] child 1842 said into stderr: "PHP Warning:  Cannot modify header information - headers already sent in /var/www/html/wp-content/plugins/some-plugin/plugin.php on line 88"
[27-Dec-2025 10:12:41] WARNING: [pool www] child 1842 said into stderr: "PHP Fatal error:  Uncaught TypeError: json_encode(): Argument #2 ($flags) must be of type int, string given in /var/www/html/wp-content/plugins/some-plugin/api.php:55"

Ce que signifie la sortie : Cette erreur fatale peut transformer une requête API en 500 et WordPress affichera « REST API encountered an error. »

Décision : Faites un rollback du plugin/thème, corrigez le code ou fixez la version de PHP si une incompatibilité a été introduite. Corriger le fatal est non négociable.

Task 13: Inspect Nginx/Apache access logs for status and upstream timing

cr0x@server:~$ sudo tail -n 20 /var/log/nginx/access.log | egrep 'wp-json|rest_route' | tail -n 5
203.0.113.10 - - [27/Dec/2025:10:13:02 +0000] "GET /wp-json/ HTTP/2.0" 200 2534 "-" "curl/8.5.0"
203.0.113.10 - - [27/Dec/2025:10:13:05 +0000] "POST /wp-json/wp/v2/posts HTTP/2.0" 401 214 "-" "Mozilla/5.0"
203.0.113.10 - - [27/Dec/2025:10:13:08 +0000] "GET /index.php?rest_route=/wp/v2/users/me HTTP/2.0" 200 612 "-" "Mozilla/5.0"

Ce que signifie la sortie : Le GET public fonctionne, le POST authentifié échoue avec 401, mais une requête authentifiée via rest_route fonctionne (peut-être avec des cookies). Cela indique des différences dans le flux d’auth, des en-têtes supprimés ou des problèmes de nonce.

Décision : Concentrez-vous sur l’authentification : cookies, nonces, Application Passwords, en-têtes proxy ou plugins de sécurité qui traitent différemment les POST vers /wp-json.

Task 14: Confirm response headers aren’t being stripped by the proxy

cr0x@server:~$ curl -sS -D- -o /dev/null https://example.com/wp-json/ | egrep -i 'content-type|cache-control|vary|www-authenticate|set-cookie'
content-type: application/json; charset=UTF-8
cache-control: no-cache, must-revalidate, max-age=0
vary: Accept-Encoding

Ce que signifie la sortie : Pour de nombreux endpoints REST, le comportement correct de cache-control est important. L’absence de Set-Cookie sur les flux de connexion ou l’absence de WWW-Authenticate sur un 401 peut aussi signaler une mutilation d’en-têtes.

Décision : Si les en-têtes diffèrent entre l’origine et l’edge, corrigez la config du proxy (pass-through des en-têtes) ou le comportement du CDN. REST et le cache « intelligent » ne sont pas amis par défaut.

Task 15: Check if basic REST auth works with an Application Password

cr0x@server:~$ curl -sS -u "apiuser:abcd efgh ijkl mnop" https://example.com/wp-json/wp/v2/users/me | head
{"id":2,"name":"API User","url":"","description":"","link":"https:\/\/example.com\/author\/apiuser\/"}

Ce que signifie la sortie : L’authentification fonctionne et WordPress peut servir des requêtes REST authentifiées.

Décision : Si l’éditeur échoue encore mais que cela fonctionne, votre problème est probablement cookie+nonce ou CORS, pas « REST en panne ».

Task 16: Detect redirect loops and canonicalization fights

cr0x@server:~$ curl -sS -I -L -o /dev/null -w '%{url_effective} %{http_code}\n' http://example.com/wp-json/
https://www.example.com/wp-json/ 200

Ce que signifie la sortie : HTTP a été redirigé vers HTTPS et www, puis a réussi.

Décision : Si vous voyez des redirections répétées ou que ça finit sur une location non JSON, corrigez siteurl/home, les en-têtes proxy (X-Forwarded-Proto) et les règles canoniques dans l’edge et WordPress.

Task 17: Check CORS headers for headless frontends

cr0x@server:~$ curl -sS -D- -o /dev/null -X OPTIONS \
  -H 'Origin: https://app.example.net' \
  -H 'Access-Control-Request-Method: POST' \
  https://example.com/wp-json/wp/v2/posts | egrep -i 'http/|access-control'
HTTP/2 403

Ce que signifie la sortie : Le prévol OPTIONS est bloqué. Beaucoup de WAF considèrent OPTIONS comme suspect. Les navigateurs refuseront la requête réelle si le prévol échoue.

Décision : Autorisez OPTIONS vers /wp-json/* à l’edge, et configurez soigneusement WordPress/plugin CORS. Confirmez aussi que vous n’exigez pas d’auth pour le prévol.

Task 18: Check PHP-FPM saturation (REST “works” until it doesn’t)

cr0x@server:~$ sudo ss -s | sed -n '1,20p'
Total: 693
TCP:   41 (estab 18, closed 12, orphaned 0, synrecv 0, timewait 12/0), ports 0

Transport Total     IP        IPv6
RAW       0         0         0
UDP       9         7         2
TCP       29        18        11
INET      38        25        13
FRAG      0         0         0

cr0x@server:~$ sudo systemctl status php8.2-fpm --no-pager | sed -n '1,20p'
● php8.2-fpm.service - The PHP 8.2 FastCGI Process Manager
     Loaded: loaded (/lib/systemd/system/php8.2-fpm.service; enabled)
     Active: active (running) since Sat 2025-12-27 09:40:12 UTC; 32min ago
   Main PID: 1021 (php-fpm8.2)
     Status: "Processes active: 28, idle: 0, Requests: 1827, slow: 12"

Ce que signifie la sortie : Zéro workers inactifs et des requêtes lentes en hausse signifie que l’API REST peut échouer sous charge avec des 502/504, en particulier pour les endpoints authentifiés.

Décision : Augmentez la capacité FPM (dans les limites de la RAM), réduisez les plugins coûteux, ajoutez du cache pour les endpoints publics et empêchez les bots d’harceler /wp-json.

Ce qui casse souvent REST (modes de défaillance réels)

1) Réécritures et permaliens : la base ennuyeuse

Si /wp-json/ renvoie 404 mais index.php?rest_route=/ fonctionne, vos réécritures sont cassées. C’est généralement l’un des cas suivants :

  • mod_rewrite d’Apache désactivé.
  • AllowOverride None bloque le .htaccess.
  • Nginx sans fallback try_files ... /index.php.
  • Règles multisite ne correspondant pas à la structure réelle du répertoire.
  • Permaliens modifiés et règles non vidées.

Conseil d’opinion : si vous êtes sur Nginx et que vous comptez sur les réécritures « automatiques » de WordPress, vous êtes déjà en train de négocier avec la physique. Mettez les bonnes règles Nginx sous contrôle de version et arrêtez de prétendre que l’UI admin contrôle votre serveur web.

2) Proxies inverses et redirections « utiles »

REST est sensible au schéma/hôte correct parce que l’authentification utilise des cookies et des nonces liés à l’origine. Si votre proxy termine TLS mais que WordPress pense être en HTTP, vous verrez :

  • boucles de redirection
  • cookies définis pour le mauvais domaine
  • échecs de validation de nonce (souvent affichés comme 401)

Les correctifs impliquent généralement de définir les bons en-têtes forwardés et de s’assurer que WordPress les respecte (ou de configurer correctement home/siteurl, plus une config adaptée au proxy).

3) WAF et protection bots qui bloquent /wp-json

Les couches de sécurité adorent bloquer les APIs JSON parce que les attaquants les aiment. Malheureusement, votre éditeur aime aussi les APIs JSON.

Déclencheurs WAF courants :

  • corps POST qui ressemblent à du HTML ou contiennent <script> dans du contenu légitime.
  • requêtes répétées d’utilisateurs connectés (Gutenberg est bavard).
  • préflight OPTIONS pour CORS.
  • limitation de débit qui ne distingue pas bots et éditeurs.

Corrigez en resserrant les exceptions : autorisez des méthodes et chemins spécifiques, logguez agressivement et limitez le périmètre. « Désactiver le WAF » n’est pas une solution ; c’est une lettre de démission.

4) Couches de cache qui traitent REST comme du contenu statique

Les endpoints publics peuvent être mis en cache avec précaution. Les endpoints authentifiés ne devraient généralement pas être mis en cache à l’edge. Quand les caches se trompent, vous voyez :

  • utilisateurs voyant les données d’autres utilisateurs (rare mais catastrophique)
  • nonces obsolètes et 401 aléatoires
  • HTML aléatoire parce que le cache a stocké une page de blocage

Règle pratique : ne jamais mettre en cache les requêtes contenant des cookies à l’edge à moins de savoir exactement ce que vous faites et de pouvoir prouver l’isolation.

5) Conflits de plugins : enregistrement de routes et filtrage des requêtes

Les plugins peuvent casser REST en :

  • enregistrant des routes incorrectement (conflits de namespace, callbacks de permission erronés)
  • filtrant rest_authentication_errors et retournant des erreurs inconditionnellement
  • émettant des espaces/blocs qui corrompent le JSON
  • changeant le content-type ou tamponnant la sortie

Si désactiver un plugin règle le problème, vous avez trouvé le coupable. Si désactiver tous les plugins règle le problème, vous avez trouvé un plugin, pas lequel. Isolez systématiquement.

6) Authentification : cookies, nonces et leurs nombreuses façons de se périmer

Gutenberg et les écrans admin utilisent souvent l’auth par cookie avec nonces. Cela signifie que le navigateur doit :

  • envoyer les bons cookies pour le bon domaine/chemin
  • envoyer un en-tête nonce valide (X-WP-Nonce)
  • ne pas être bloqué par les politiques SameSite ou des mismatches cross-domain

Quand cela échoue, l’API REST elle-même peut fonctionner, mais l’éditeur râle. Vous réglez cela en alignant domaines, schémas et comportement des cookies — pas en « réinstallant WordPress ».

7) Fiabilité backend : fatals PHP, timeouts DB, problèmes de stockage

Les requêtes REST exécutent des chemins de code WordPress. Elles touchent la base de données. Elles lisent/écrivent des options. Elles peuvent générer des miniatures. Elles peuvent parler à des APIs externes. Si votre stockage est lent ou que votre DB est mécontente, les endpoints REST échoueront en premier parce qu’ils sont invoqués constamment par les fonctionnalités modernes de WordPress.

Note d’ingénieur stockage : les échecs REST « aléatoires » sous charge corrèlent souvent avec des pics de latence I/O, surtout sur de l’hébergement mutualisé ou des disques réseau sous-dimensionnés. Contrôlez l’attente I/O et la santé DB avant d’accuser REST d’être « instable ».

Blague #2 : Si votre API REST ne tombe qu’un lundi, elle n’est pas hantée — c’est votre planning de déploiement qui porte un masque.

Trois mini-récits d’entreprise issus du terrain

Mini-récit 1 : L’incident causé par une mauvaise supposition

Une société de taille moyenne exécutait WordPress derrière un proxy inverse et un CDN. Ils ont déployé une nouvelle page d’atterrissage headless qui récupérait des posts via REST. En staging, tout fonctionnait. En production, l’éditeur a commencé à échouer à publier et la page headless retournait parfois 401. Tout le monde a blâmé « WordPress qui fait WordPress ».

La mauvaise supposition était simple : « Si le site charge, l’API doit être OK. » La homepage était mise en cache par le CDN. Les appels REST ne l’étaient pas — sauf parfois, parce que le CDN avait une règle générique qui mettait en cache « toutes les requêtes GET ». Les GET publics REST se sont retrouvés mis en cache. Les GET authentifiés furent aussi mis en cache, car la clé cache ignorait les cookies pour la performance.

Le résultat fut un chaos intermittent : la réponse authentifiée d’un éditeur pouvait être servie à un autre éditeur (parfois bloquée par des contrôles de capacité, parfois non). L’équipe n’a pas remarqué de fuite de données, mais a constaté des sessions rompues et des échecs de nonce parce que les réponses mises en cache ne correspondaient pas aux attentes du navigateur.

Le correctif n’était pas héroïque. Ils ont changé la politique CDN : bypasser le cache pour toute requête avec Cookie et pour tout chemin sous /wp-json/ sauf whitelist explicite. Puis ils ont whitelisté seulement quelques endpoints publics avec une clé cache sûre et un TTL court. Le REST s’est stabilisé instantanément.

Leçon : ne jamais supposer que « le web marche » implique « l’API marche », surtout quand le cache est impliqué. Votre homepage ment ; curl dit la vérité.

Mini-récit 2 : L’optimisation qui s’est retournée contre eux

Une équipe marketing d’entreprise voulait un TTFB plus rapide. Un ingénieur a activé des optimisations HTML agressives et la compression des réponses au proxy inverse. Il a aussi activé un jeu de règles « durcissement sécurité » pour bloquer les « query strings suspectes ».

La performance s’est améliorée pour le site public. Puis Gutenberg a commencé à échouer sur les uploads d’images et les mises à jour de posts. Le panneau Site Health de WordPress indiquait des erreurs de l’API REST. Les développeurs ont commencé à chasser des conflits de plugins fantômes.

Le vrai problème : l’optimisation proxy incluait une règle qui tamponnait et réécrivait les réponses et imposait une petite client_max_body_size. Les uploads médias via REST rencontraient des 413. Pendant ce temps, la règle « query string suspecte » signalait les patterns rest_route= et bloquait les requêtes de fallback utilisées par certains clients et plugins.

Le correctif fut embarrassantement banal : augmenter les limites de taille de corps sur les blocs location concernés, désactiver la réécriture pour les réponses JSON et ajuster les règles WAF pour les routes REST connues. Ils ont aussi ajouté du monitoring spécifique pour /wp-json/ afin de détecter les régressions avant que le marketing ne le remarque.

Leçon : les optimisations sont permises. Mais elles nécessitent une mentalité allowlist pour les APIs. Si votre proxy modifie les payloads, vous faites du travail applicatif sans tests applicatifs.

Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la situation

Une organisation globale exécutait plusieurs instances WordPress avec une infrastructure-as-code identique pour Nginx et PHP-FPM. Leur équipe SRE appliquait une politique ennuyeuse : chaque changement de config avait un canary, et chaque canary avait une vérification synthétique qui frappait /wp-json/, un endpoint public et un endpoint authentifié.

Un jour, un changement Nginx mineur destiné à durcir la sécurité a accidentellement supprimé le fallback try_files et transformé les chemins inconnus en 404. Le site public continuait de fonctionner car la plupart des pages étaient en cache et les routes communes existaient. Mais les appels REST ont échoué immédiatement sur le canary.

Les vérifications synthétiques l’ont détecté en quelques minutes. Ils ont rollbacké avant le déploiement complet. Pas de tickets clients. Pas d’archéologie paniquée sur Slack. Juste un revert propre et un petit post-mortem.

Leçon : les pratiques « ennuyeuses » — canaries, checks synthétiques et config en contrôle de version — vous protègent de l’ingéniosité. REST est une excellente cible canary car il exerce rapidement routage, PHP et logique applicative.

Erreurs courantes : symptôme → cause racine → correction

Cette section est pensée pour être utilisée sous pression. Trouvez votre symptôme et appliquez la correction. Ne débattez pas philosophie pendant votre outage.

1) WordPress dit « REST API encountered an error », et /wp-json renvoie de l’HTML

  • Symptôme : content-type: text/html, peut-être une page de connexion ou une page de blocage.
  • Cause racine : blocage WAF/CDN, redirection vers la page de login, ou template d’erreur mis en cache.
  • Correction : Contournez l’edge vers l’origine et comparez. Désactivez le caching pour /wp-json. Ajoutez des règles allow WAF pour les chemins/méthodes REST. Confirmez qu’aucune redirection d’auth n’est appliquée aux routes API.

2) /wp-json fonctionne, mais les endpoints authentifiés renvoient 401 dans l’éditeur

  • Symptôme : Endpoints publics OK ; actions éditeur échouent ; « nonce invalide » aléatoire.
  • Cause racine : mismatch domaine/schéma des cookies, problèmes SameSite, proxy n’envoyant pas X-Forwarded-Proto correct, ou mise en cache des nonces.
  • Correction : Alignez home et siteurl. Corrigez les en-têtes proxy et les redirections canoniques. Assurez-vous que les requêtes REST authentifiées ne sont mises en cache nulle part.

3) /wp-json renvoie 404 mais index.php?rest_route fonctionne

  • Symptôme : Le chemin réécrit échoue, la requête directe fonctionne.
  • Cause racine : Mauvaise config de réécriture Nginx/Apache, .htaccess ignoré, ou absence de fallback try_files.
  • Correction : Corrigez la config serveur. Videz les réécritures ensuite. Retestez les deux endpoints jusqu’à obtenir un comportement identique.

4) Les requêtes REST renvoient 403 uniquement pour POST/PUT/DELETE

  • Symptôme : GET fonctionne ; écritures bloquées.
  • Cause racine : règle WAF bloquant des méthodes, faux positifs mod_security CRS, ou restrictions de méthode sur le proxy.
  • Correction : Consultez les logs WAF pour l’ID de règle et ajustez des exceptions étroites pour /wp-json. Assurez-vous que le proxy autorise ces méthodes vers l’origine.

5) REST renvoie 500 de façon intermittente, les logs montrent des fatals PHP

  • Symptôme : 500/502 avec traces de pile dans les logs PHP.
  • Cause racine : bug plugin/thème ou incompatibilité de version PHP.
  • Correction : Rollback du changement qui a introduit les fatals. Patch du plugin. Ajout de tests automatisés ou au moins de checks synthétiques REST dans les pipelines de déploiement.

6) REST est lent ; parfois 504 Gateway Timeout

  • Symptôme : Temps de réponse longs, puis timeouts sous charge.
  • Cause racine : épuisement des workers PHP-FPM, contention DB, stockage lent, appels API externes dans le chemin de requête.
  • Correction : Profilez les requêtes lentes ; augmentez la capacité FPM prudemment ; optimisez la DB et le cache d’objets ; ajoutez timeouts/limiters pour les appels externes ; bloquez les clients abusifs.

7) Erreurs CORS dans la console du navigateur, curl réussit

  • Symptôme : Le navigateur bloque les requêtes ; curl réussit.
  • Cause racine : en-têtes CORS manquants/bloqués, OPTIONS bloqué, en-têtes Origin erronés.
  • Correction : Autorisez OPTIONS à travers WAF/proxy et configurez correctement Access-Control-Allow-Origin et les en-têtes associés, idéalement avec une allowlist.

Checklists / plan étape par étape

Checklist A: Vous avez une erreur REST maintenant

  1. Exécutez curl vers /wp-json/ et capturez le statut, les en-têtes et le type de corps (JSON vs HTML).
  2. Exécutez curl vers /index.php?rest_route=/ et comparez.
  3. Testez depuis l’hôte d’origine avec un en-tête Host: pour contourner l’edge.
  4. Si l’edge est impliquée : vérifiez les logs WAF/CDN pour blocs, limites de débit et restrictions de méthode.
  5. Si les réécritures sont impliquées : inspectez le try_files Nginx ou AllowOverride/mod_rewrite d’Apache.
  6. Si l’auth est impliquée : testez Application Password ; puis testez les flux éditeur cookie+nonce.
  7. Si 500/timeout : vérifiez les logs PHP, la saturation FPM, la santé DB et les changements récents de déploiement/plugin.

Checklist B: Prévenir le prochain incident (hygiène opérationnelle)

  1. Ajoutez des checks synthétiques pour /wp-json/, un endpoint public et un endpoint authentifié.
  2. Instrumentez les codes de statut pour /wp-json dans les logs d’accès et alertez sur les pics 4xx/5xx.
  3. Mettez les configs Nginx/Apache en contrôle de version ; gatez les changements avec un canary.
  4. Définissez explicitement la politique de cache : quels endpoints REST sont cacheables et comment les clés de cache sont construites.
  5. Créez un process d’exception WAF : allowlists par chemin/méthode, suivi des ID de règle et revues d’expiration.
  6. Gardez les mises à jour de plugins en staging ; monitorisez les checks synthétiques REST après déploiement.

Checklist C: Quand vous suspectez un conflit de plugin

  1. Faites un snapshot de la liste de plugins actifs.
  2. Désactivez un plugin à haut risque (sécurité/cache) à la fois.
  3. Retestez /wp-json/ et la route en échec.
  4. Quand ça marche, réactivez les autres un par un pour isoler.
  5. Appliquez une correction de configuration ou remplacez le plugin s’il ne peut coexister avec vos besoins.

Faits et histoire intéressants (contexte court et utile)

  • Fait 1 : L’API REST WordPress a commencé comme un plugin fonctionnalité avant d’être fusionnée dans le core, d’où certains anciens articles qui parlent encore du « plugin REST API ».
  • Fait 2 : Le point de base /wp-json/ est un document index — une « table des matières » de l’API — donc un index cassé est souvent un problème de routage ou de blocage, pas « posts manquants ».
  • Fait 3 : Gutenberg (l’éditeur de blocs) dépend fortement du REST. Si REST est instable, l’éditeur est généralement le premier à se plaindre.
  • Fait 4 : WordPress peut servir REST via un paramètre de requête (rest_route), ce qui en fait un excellent outil pour distinguer les problèmes de réécriture des problèmes applicatifs.
  • Fait 5 : De nombreux produits de sécurité ont historiquement traité les endpoints JSON comme des « surfaces API » et appliqué des inspections plus strictes, d’où des faux positifs fréquents autour de /wp-json.
  • Fait 6 : Les routes REST sont enregistrées par le code à l’exécution. Si un plugin ne se charge pas (fatal error), des namespaces entiers de routes peuvent disparaître, transformant des 200 attendus en 404.
  • Fait 7 : L’authentification WordPress pour les appels REST côté navigateur utilise souvent des nonces plutôt que Basic Auth ; les nonces sont sensibles au temps et faciles à casser par le cache ou le décalage d’horloge.
  • Fait 8 : Historiquement, certains hébergeurs et plugins bloquaient les chemins /wp-json/wp/v2/users pour réduire le risque d’énumération, parfois en bloquant trop large et en cassant des actions admin légitimes.
  • Fait 9 : Les politiques de cache CDN qui ignorent les cookies peuvent provoquer des fuites de réponses API entre sessions — rare, mais cela s’est déjà produit suffisamment pour que les SREs chevronnés soient nerveux à propos de ça.

FAQ

1) Comment savoir si l’API REST WordPress est en panne ou si c’est juste l’éditeur ?

Exécutez curl -D- https://example.com/wp-json/. Si c’est 200 avec du JSON, REST est opérationnel. Si l’éditeur échoue, concentrez-vous sur l’auth/nonce/cookies et le cache.

2) Pourquoi /wp-json fonctionne mais /wp-json/wp/v2/posts échoue ?

L’index peut se charger tandis que des routes spécifiques échouent à cause du code de plugin/thème, des callbacks de permission ou de l’enregistrement des routes. Vérifiez les logs PHP et désactivez des plugins pour isoler.

3) Quel est le test de réécriture le plus rapide ?

Comparez /wp-json/ avec /index.php?rest_route=/. Si seul ce dernier fonctionne, votre serveur web a un problème de réécriture.

4) Pourquoi reçois-je 401 Unauthorized alors que je suis connecté dans wp-admin ?

Typiquement mismatch cookie/nonce dû à une incohérence de domaine ou de schéma (www vs non-www, http vs https), comportement SameSite des cookies, ou proxy qui rapporte mal HTTPS.

5) Comment savoir si le WAF bloque REST ?

Cherchez un 403 avec des pages HTML de blocage, des en-têtes CDN/WAF, ou des blocs qui n’arrivent que sur POST/OPTIONS. Contournez l’edge vers l’origine ; si l’origine fonctionne, c’est le WAF/CDN.

6) Puis-je mettre en cache les réponses de l’API REST ?

Oui, de manière sélective. Mettez en cache uniquement les endpoints vraiment publics et non personnalisés. Ne mettez jamais en cache les requêtes avec cookies à l’edge à moins de prouver que les clés cache et l’isolation sont sûres.

7) Pourquoi vois-je des erreurs CORS mais curl réussit ?

Les navigateurs appliquent CORS et envoient souvent un prévol OPTIONS. Si OPTIONS est bloqué ou si les en-têtes CORS sont incorrects, le navigateur refuse. Curl ne s’en soucie pas.

8) Désactiver des plugins prouve-t-il toujours que c’est un plugin ?

Ça prouve une interaction, pas forcément la culpabilité. Un plugin peut déclencher une règle proxy/WAF ou révéler un problème de config latent. Utilisez la désactivation pour réduire la portée, puis trouvez la vraie cause.

9) Quelle est la méthode d’auth la plus sûre pour des appels REST serveur-à-serveur ?

Les Application Passwords sont généralement les moins pénibles dans WordPress core. Utilisez HTTPS, restreignez la portée, faites tourner les identifiants et surveillez les abus.

10) Pourquoi REST échoue-t-il seulement sous charge ?

Parce qu’à la charge vous atteignez des limites : workers PHP-FPM, connexions DB, latence I/O, ou rate limiting. Les endpoints REST sont chatty et amplifient rapidement les goulots d’étranglement.

Conclusion : prochaines étapes qui réduisent vraiment la récurrence

Si vous ne faites que quelques actions à partir d’ici, faites celles-ci :

  1. Rendez /wp-json/ observable. Ajoutez des checks synthétiques et des alertes sur les codes de statut et la latence. REST est le canary du WordPress moderne.
  2. Séparez routage, auth et santé backend. Utilisez le test rest_route pour prouver les réécritures, puis testez les endpoints publics, puis les endpoints authentifiés.
  3. Cessez de laisser les middleboxes improviser. Définissez explicitement le comportement de cache et WAF pour /wp-json. Le default-deny est acceptable ; le default-surprise ne l’est pas.
  4. Mettez la config serveur sous contrôle de version. Si la disponibilité REST dépend d’un bout de snippet Nginx collé il y a trois ans, vous n’avez pas une config — vous avez du folklore.

Faites maintenant la chose ingrate : lancez les trois premières tâches, notez le comportement observé et suivez la couche indiquée par la réalité. Les pannes WordPress REST sont rarement mystérieuses. Elles sont juste réparties sur suffisamment de composants pour que les gens confondent « compliqué » et « inconnaissable ».

← Précédent
VPN de bureau : autoriser l’accès à un seul serveur/port (moindre privilège)
Suivant →
Paramètres Cloudflare pour WordPress qui n’interrompent pas wp-admin

Laisser un commentaire