Ce message est produit par api-fetch, la bibliothèque que l’éditeur de blocs utilise pour parler à l’API REST de WordPress. Il n’apparaît que dans un cas : le serveur a répondu, et le corps de la réponse n’a pas pu être lu comme du JSON. L’éditeur n’est pas en cause. C’est l’API REST, ou ce qui se trouve devant elle, qui a renvoyé autre chose que ce qu’elle devait.

La réponse en trois lignes

Ouvrez https://votre-site.fr/wp-json/wp/v2/types dans un navigateur, connecté ou non. Si vous voyez une page de votre site ou une erreur 404 au lieu d’un texte qui commence par {"post":, allez dans Réglages → Permaliens et cliquez sur Enregistrer sans rien changer. Si le JSON s’affiche bien, la cause est devant WordPress : voyez les causes 2, 3 et 6.

Ce que le message dit, mot pour mot

Chaque enregistrement dans l’éditeur est une requête envoyée à l’API REST, dont la réponse est lue par response.json(). Dans WordPress 6.9.4, ce code est dans wp-includes/js/dist/api-fetch.js, fonction parseJsonAndNormalizeError : si la lecture échoue, elle lève une erreur invalid_json dont le texte d’origine est The response is not a valid JSON response. L’éditeur place devant « La publication a échoué. » ou « La mise à jour a échoué. », puis ajoute ce texte.

Trois précisions, lues dans le même fichier :

  • Le code de réponse ne compte pas. Une réponse en erreur passe par la même lecture qu’une réponse réussie. Un 403 en HTML et un 200 en HTML donnent le même message.
  • Une erreur en JSON donne un autre message, celui de l’erreur elle-même. Ce message signifie donc précisément que le corps n’était pas du JSON.
  • Pas de réponse du tout donne aussi autre chose : « Impossible d’obtenir du serveur une réponse valide. » Ici, le serveur a bien répondu.

Où est l’API, et comment la vérifier

Le préfixe de l’API est wp-json, fixé par rest_get_url_prefix() dans wp-includes/rest-api.php. Avec des permaliens actifs, get_rest_url() construit https://votre-site.fr/wp-json/ à partir de l’adresse du site, l’option home. Avec les permaliens « Simple », il n’y a aucune réécriture et l’adresse devient https://votre-site.fr/index.php?rest_route=/wp/v2/types.

L’éditeur reçoit cette adresse par une ligne injectée dans la page d’administration, wp.apiFetch.createRootURLMiddleware(…), écrite par wp-includes/script-loader.php. Vous pouvez la lire sans être connecté : le code source de toute page publique contient <link rel="https://api.w.org/" href="…">, émis par rest_output_link_wp_head(). C’est l’adresse à tester.

Ajoutez-lui wp/v2/types. Ce point d’entrée répond à tout le monde en lecture : sa vérification de droits ne refuse que le contexte edit. Il se teste déconnecté :

curl -sI https://votre-site.fr/wp-json/wp/v2/types

Une réponse de l’API porte deux en-têtes envoyés par WP_REST_Server::serve_request() : Content-Type: application/json; charset=UTF-8 et X-Robots-Tag: noindex. C’est sa signature. Une réponse qui ne les porte pas ne vient pas du serveur REST de WordPress. Sans terminal, le testeur d’en-têtes les lit pour vous.

Lire le résultat

  • Une page de votre site, avec votre thème, ou sa page 404. WordPress a tourné sans reconnaître l’adresse de l’API. Cause 1.
  • Une page nue en anglais, 403, 401 ou 404, sans votre thème. La requête a été arrêtée avant WordPress. Causes 3 et 7. Ces signatures sont décrites dans le guide sur l’erreur 500.
  • Un code 301 ou 302 avec un en-tête Location. L’adresse de l’API n’est pas celle que le serveur sert. Cause 2.
  • Du JSON précédé d’une ligne vide ou d’un texte. Une extension écrit avant la réponse. Cause 4.
  • Un JSON d’erreur du type {"code":"rest_…"}. L’API fonctionne en lecture. Causes 3 et 5 : ce sont les écritures qui sont bloquées.

Les causes, par ordre de fréquence

1. Les règles de réécriture manquent

Au démarrage, rest_api_register_rewrites() déclare une règle qui transforme wp-json/… en index.php?rest_route=/…. Elle n’existe que dans la mémoire de WordPress. Pour qu’une requête sur /wp-json/ atteigne index.php, le serveur web doit la lui passer. Sur Apache, ce sont les lignes du bloc # BEGIN WordPress du fichier .htaccess, écrites par save_mod_rewrite_rules() dans wp-admin/includes/misc.php. Cette fonction n’écrit rien si le fichier n’est pas accessible en écriture ou si mod_rewrite n’est pas détecté. L’écran Permaliens affiche alors « Vous devez mettre à jour votre fichier .htaccess maintenant. »

Vérification. Le bloc de .htaccess doit contenir RewriteRule . /index.php [L]. Sur nginx, WordPress n’écrit jamais rien : la configuration du serveur doit renvoyer les adresses inconnues vers index.php, avec une directive try_files.

Réparation. Réglages → Permaliens → Enregistrer, sans rien changer : WordPress réécrit le bloc s’il le peut. Sinon, recopiez-le à la main. Un test rapide lève le doute : passez en permaliens « Simple ». L’API utilise alors ?rest_route=, sans réécriture, et l’éditeur doit enregistrer à nouveau.

2. Une redirection de domaine change l’adresse de l’API

get_rest_url() part de l’option home, et ne la passe en HTTPS que si is_ssl() est vrai. Or cette fonction, dans wp-includes/load.php, ne regarde que $_SERVER['HTTPS'] et le port 443. Elle ignore l’en-tête X-Forwarded-Proto d’un proxy TLS. Si l’adresse du site est en http:// ou sans www alors que le navigateur affiche l’administration en https://www., l’éditeur appelle une adresse que le serveur redirige, et la réponse n’est pas celle de l’API.

Vérification. Comparez l’adresse du <link rel="https://api.w.org/"> avec votre barre d’adresse. Un 301 ou 302 au curl -sI confirme. Si le navigateur, lui, n’affiche plus rien du tout, la même incohérence a tourné en boucle de redirection.

Réparation. Mettez « Adresse web de WordPress » et « Adresse web du site » sur l’adresse exacte servie, protocole et www compris. Sans accès à l’administration, les constantes WP_HOME et WP_SITEURL de wp-config.php ont priorité sur les options. Derrière un proxy TLS, ajoutez avant elles :

if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] ) && 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
	$_SERVER['HTTPS'] = 'on';
}

3. Un pare-feu bloque /wp-json/

ModSecurity chez l’hébergeur, un pare-feu applicatif, un CDN avec page de vérification : tous répondent à la place de WordPress, en HTML, souvent en 403. Le cas le plus trompeur ne bloque que les écritures : la lecture de /wp/v2/types passe, l’enregistrement échoue. Testez donc aussi une écriture, sans être connecté :

curl -s -X POST -H 'Content-Type: application/json' -d '{}' https://votre-site.fr/wp-json/wp/v2/posts

La réponse attendue est un refus en JSON, rest_cannot_create en 401 : la requête a atteint WordPress, qui a refusé faute d’identification. Une page HTML en 403 désigne le pare-feu.

Une nuance : l’éditeur envoie l’en-tête Accept: application/json, et wp_die() répond alors en JSON. Une extension de sécurité qui refuse proprement produit donc un autre message ; celle qui affiche une page et coupe l’exécution produit celui-ci.

Réparation. Dans l’extension de sécurité, exclure /wp-json/ de ses règles. Chez l’hébergeur, demander l’identifiant de la règle ModSecurity déclenchée, lisible dans leur journal, et sa désactivation pour le site.

4. Une extension écrit avant la réponse

Un avertissement PHP affiché, une ligne vide avant <?php ou après ?>, un marqueur d’ordre d’octets déposé par un éditeur de texte : le corps commence alors par autre chose que {, et la lecture JSON échoue au premier caractère. Le même défaut produit des pages blanches.

Vérification. Affichez le code source de /wp-json/wp/v2/types. Le premier caractère doit être {. Un avertissement PHP nomme son fichier et sa ligne, et le chemin contient le dossier de l’extension fautive. Réparation. Corrigez le fichier ou désactivez l’extension. En attendant, define( 'WP_DEBUG_DISPLAY', false ); dans wp-config.php coupe l’affichage des avertissements, sans traiter la cause.

5. L’API REST « désactivée » par une extension

Depuis WordPress 4.7, l’API ne peut plus être coupée entièrement. Le code de serve_request() le dit en toutes lettres : seul le filtre rest_authentication_errors permet d’en restreindre l’accès. Une extension qui l’utilise renvoie une erreur en JSON, avec son propre message. Celle qui redirige les requêtes de l’API vers l’accueil, ou affiche une page, produit le message « JSON valide ».

Vérification. /wp-json/ répond par une redirection ou une page alors que les permaliens sont en place. Réparation. L’option qui désactive l’API REST doit au minimum autoriser les utilisateurs connectés. L’éditeur de blocs ne fonctionne pas sans elle.

6. Un cache ou un proxy remplace la réponse

api-fetch ne lit jamais l’en-tête Content-Type : il tente la lecture du corps quoi qu’il arrive. Un en-tête réécrit ne déclenche donc rien à lui seul. Mais il est un indice : un Content-Type: text/html sur /wp-json/ prouve que la réponse n’a pas été écrite par le serveur REST : page d’erreur mise en cache, ou réponse servie par un CDN à sa place.

Vérification. Cherchez les en-têtes X-Cache, Age ou CF-Cache-Status dans la réponse. Réparation. Excluez /wp-json/ et index.php?rest_route= des règles de cache et des pages de vérification, puis videz le cache.

7. Une authentification HTTP sur le site

Une protection par mot de passe posée par le serveur répond 401 avec sa propre page HTML dès que le navigateur n’envoie pas les identifiants. Il ne les renvoie que pour l’espace protégé et la même origine. Le cas se rencontre surtout quand l’adresse de l’API n’est pas celle sur laquelle vous vous êtes identifié : la cause 2 se cache derrière.

Vérification. curl -sI sur l’adresse de l’API répond 401 avec un en-tête WWW-Authenticate. Réparation. Alignez l’adresse du site sur l’adresse protégée, ou limitez la protection à /wp-admin/.

Deux fausses pistes

Le nonce expiré. Chaque requête porte un en-tête X-WP-Nonce, vérifié par rest_cookie_check_errors(). S’il est périmé, l’API répond en JSON rest_cookie_invalid_nonce, et api-fetch en redemande un à admin-ajax.php?action=rest-nonce avant de rejouer la requête, sans rien afficher. Un nonce ne produit jamais ce message.

Le plantage PHP. Une erreur fatale pendant l’enregistrement passe par le gestionnaire d’erreurs de WordPress, qui répond en JSON quand la requête en demande. L’éditeur affiche alors « La publication a échoué. » seul. La marche à suivre est celle de l’erreur critique.

Vérifier que c’est terminé

Rechargez l’éditeur et enregistrez un brouillon. Puis relevez la réponse de l’API une dernière fois :

curl -sI https://votre-site.fr/wp-json/

Attendu : 200, Content-Type: application/json, X-Robots-Tag: noindex. Les trois ensemble prouvent que la réponse vient de WordPress, et qu’elle arrive entière.

Si la réparation doit durer

Ce défaut n’empêche pas les visiteurs de lire le site : seuls les enregistrements échouent. Mais toucher au .htaccess ou à l’adresse du site peut le rendre indisponible à chaque essai. Mieux vaut alors passer volontairement en maintenance avec un statut 503 le temps des essais, et rouvrir une fois l’enregistrement revenu.