Error de la REST API de WordPress: qué rompe REST y cómo solucionarlo

¿Te fue útil?

Algunas fallas de WordPress son escandalosas: pantallas en blanco, errores fatales, el tipo que notas antes de que se enfríe tu café. Las fallas de la REST API son más silenciosas y más molestas. Tu editor no guarda, tu frontend sin cabeza se queda en blanco, WooCommerce procesa pedidos “a veces”, y la única pista es un mensaje presumido como “The REST API encountered an error.”

Aquí está la realidad práctica: la REST API no es una sola característica. Es una cadena: reescrituras, enrutamiento, autenticación, cookies/nonces, capas de caché, middleware de seguridad y tu pila de hosting—cada una puede romperse de forma independiente. Vamos a aislar qué es lo que falló, con comandos que puedes ejecutar, salidas que puedes interpretar y decisiones que puedes tomar sin adivinar.

Cómo funciona realmente la REST de WordPress (y dónde falla)

Cuando alguien dice “la REST API de WordPress está rota”, normalmente se refiere a una de estas cosas:

  • No se alcanza la ruta (reescrituras, enlaces permanentes, configuración del servidor, proxy inverso, CDN o WAF en el camino).
  • Se alcanza la ruta pero está bloqueada (fallos de autenticación, problemas con nonce/cookies, comprobaciones de capacidades, plugins de seguridad o reglas del WAF).
  • La ruta se ejecuta y falla (fatal de PHP, timeouts de BD, límites de memoria, ruta de código de un plugin, errores de serialización).
  • La ruta se ejecuta pero la respuesta se corrompe (caché, compresión, cambios de content-type, redirecciones inesperadas, contenido mixto, CORS).

En un sitio sano, el endpoint base canónico es:

  • /wp-json/ (un índice JSON de namespaces y rutas)
  • Ruta de ejemplo: /wp-json/wp/v2/posts

En el fondo, WordPress usa reglas de reescritura para dirigir /wp-json/... a index.php, donde el servidor REST despacha a las rutas registradas. Eso significa que incluso las fallas “del API” a menudo empiezan como aburridos fallos de reescritura.

Además: las solicitudes REST son solo solicitudes HTTP. Esa es la ventaja y la maldición. Cada caja intermedia que alguna vez “ayudó” a un sitio web (caché, CDN, firewall, bloqueador de bots, filtro gzip, minificador, “endurecedor de seguridad”) puede “ayudar” a tu REST API hasta enterrarla.

Una frase operativa para mantenerte honesto:

“La esperanza no es una estrategia.” — Gen. Gordon R. Sullivan

Dónde se agrupan las fallas

En producción, los problemas de la REST API se concentran alrededor de cinco capas:

  1. DNS/TLS/borde: certificados, desajustes SNI, redirecciones HTTP→HTTPS, caché del CDN, bloqueos del WAF.
  2. Proxy inverso / servidor web: reescrituras en Nginx/Apache, normalización de paths, eliminación de cabeceras, límites de tamaño de cuerpo.
  3. Capa de aplicación de WordPress: ajustes de enlaces permanentes, registro de rutas, conflictos de plugins, lógica de auth y nonce.
  4. Estado y almacenamiento: bloqueos/timeouts de BD, rarezas del object cache, permisos de sistema de archivos.
  5. Comportamiento del cliente: frontend JS, editor Gutenberg, CORS, cookies mixtas, nonces obsoletos.

No investigues primero desde la interfaz de administración de WordPress. Eso es como diagnosticar una caída de red mirando el fondo de pantalla de tu portátil. Empieza haciendo una solicitud desde fuera del navegador con entradas y salidas conocidas.

Guía rápida de diagnóstico

Cuando la REST está “caída”, quieres una respuesta en minutos: ¿esto es borde/proxy, app o backend? Usa esta guía en orden. No te saltes pasos porque “ya lo sabes”. Ahí es donde los incidentes se convierten en memorias.

Primero: confirma el endpoint y la verdad HTTP

  1. Solicita /wp-json/ con curl y registra: código de estado, content-type y cualquier redirección.
  2. Compara desde dos puntos: desde tu portátil y desde el servidor (o una máquina dentro de la misma red que WordPress).
  3. Revisa si recibes HTML en lugar de JSON. HTML suele significar que estás obteniendo una página de login, una página de bloqueo del WAF o una plantilla de error cacheada.

Segundo: aísla borde/seguridad de WordPress

  1. Evita el CDN si es posible (host de origen directo, VIP del balanceador interno o override del archivo hosts en un entorno controlado).
  2. Revisa los logs del WAF/plugin de seguridad por bloqueos en /wp-json y por firmas comunes (falsos positivos de SQLi/XSS).
  3. Confirma que no haya bucles de redirección forzados (HTTP→HTTPS→HTTP, www→sin-www, reglas de slash final).

Tercero: prueba el enrutamiento y los permalinks de WordPress

  1. Verifica que los permalinks no estén en “Plain”. REST puede funcionar en modo plain, pero las reescrituras suelen quedar inconsistentes cuando la gente cambia ajustes sin vaciar reglas.
  2. Confirma que existan reescrituras en la configuración del servidor (reglas de location en Nginx) o en .htaccess (Apache).
  3. Comprueba si index.php?rest_route=/ funciona. Si sí, las reescrituras están rotas; si no, es problema de app o de bloqueo.

Cuarto: autentica solo después de que la ruta funcione

  1. Prueba endpoints públicos primero (lista de posts) antes de endpoints privados (usuarios, ajustes).
  2. Si se necesita auth, prueba con Application Passwords o Basic Auth (en un entorno seguro) para eliminar la complejidad de nonce/cookie.
  3. Luego prueba los flujos cookie+nonce para problemas del editor.

Quinto: si es lento o devuelve 500, trátalo como saturación del backend

  1. Correlaciona con saturación de PHP-FPM, queries lentas de BD y logs de errores.
  2. Toma una muestra de solicitudes fallidas y traza: log de acceso del servidor web → log de errores de PHP → log de depuración de WP → logs de BD.
  3. Decide: revertir un cambio de plugin/tema, escalar o mitigar con excepciones de caché.

Regla de decisión: Si /wp-json/ no devuelve un 200 con JSON desde dos puntos, no pierdas tiempo depurando funciones de WordPress. Arregla el enrutamiento/borde primero.

Mapa síntoma→capa: decodifica errores según el comportamiento HTTP

Los errores REST a menudo aparecen como un aviso genérico en el administrador de WordPress. Ignora el aviso y mira el HTTP.

Códigos de estado y lo que suelen significar

  • 200 pero content-type incorrecto (HTML): has alcanzado una página de login, una página de bloqueo del WAF, una plantilla de error cacheada o el destino de una redirección.
  • 301/302 en bucle: reglas de canonicalización en conflicto (HTTP/HTTPS, www/sin-www, slash final), o cabeceras de proxy mal configuradas.
  • 401 Unauthorized: credenciales faltantes/invalidas, fallo de nonce, Application Passwords deshabilitadas, plugin de seguridad interceptando.
  • 403 Forbidden: regla de WAF/CDN, mod_security, restricciones por IP, “bloquear solicitudes JSON” o una comprobación de capacidad fallida.
  • 404 Not Found: reescrituras no funcionan, mismatch de location en Nginx, problemas de multisite con rutas, o rutas no registradas.
  • 405 Method Not Allowed: proxy/servidor niega POST/PUT, o la capa de caché solo permite GET.
  • 413 Payload Too Large: límites de tamaño de cuerpo en el proxy; las subidas REST fallan, endpoints de media fallan.
  • 429 Too Many Requests: limitador de tasa, WAF, protección anti-bots o plugin con throttling agresivo.
  • 500/502/503: fatal de PHP, fallo del upstream, php-fpm exhausto, timeouts de BD, origen caído o timeouts de gateway.

Broma #1: La REST API es “stateless”, lo cual es adorable—como llamar “tranquilo” a un niño cinco minutos antes de que le dé el subidón de azúcar.

Tareas prácticas (comandos, salidas, decisiones)

Estos no son “consejos”. Son tareas repetibles. Cada una incluye un comando, qué significa la salida y qué decisión tomar a continuación.

Tarea 1: Comprueba el índice REST desde tu estación de trabajo

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

Qué significa la salida: Obtuviste un 200 y content-type JSON. Bien: el enrutamiento y la ruta básica funcionan desde fuera.

Decisión: Pasa a las comprobaciones relacionadas con auth y plugins. Si ves 301/302, sigue las redirecciones (-L) e inspecciona la canonicalización. Si el content-type es text/html, no estás recibiendo la REST API.

Tarea 2: Verifica si recibes HTML en lugar de 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>

Qué significa la salida: 403 con una página HTML de bloqueo y una cabecera de CDN/servidor. Eso no es WordPress. Es seguridad en el borde.

Decisión: Deja de tocar WordPress. Investiga reglas del WAF/CDN/firewall y evita el borde para confirmar la salud del origen.

Tarea 3: Prueba desde el host de origen para evitar CDN/WAF (cuando sea posible)

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

Qué significa la salida: El origen sirve JSON correctamente. El problema está en el borde.

Decisión: Arregla la política del borde: permite /wp-json/*, permite los métodos necesarios, ajusta reglas de bots o añade una excepción de WAF para los endpoints REST (estrechamente acotada).

Tarea 4: Determina si las reescrituras están rotas usando 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

Qué significa la salida: La REST API funciona cuando evitas las reescrituras. Entonces tu capa de reescritura está rota.

Decisión: Arregla la configuración de reescritura del servidor o vacía los permalinks. No pierdas tiempo depurando plugins hasta que /wp-json/ funcione como index.php?rest_route=/.

Tarea 5: Comprueba el estado de rewrite y .htaccess en Apache

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

Qué significa la salida: Los módulos requeridos existen. Si falta rewrite, los permalinks y las rutas REST a menudo fallan.

Decisión: Si rewrite_module está ausente, actívalo y recarga Apache. Si está presente, inspecciona el vhost y las reglas de .htaccess a continuación.

Tarea 6: Valida el enrutamiento de Nginx para wp-json (trampa común)

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:    }

Qué significa la salida: Un try_files sensato con fallback a index.php enruta paths desconocidos (incluido /wp-json/) a index.php.

Decisión: Si ves try_files $uri =404; sin fallback a index, arréglalo o REST hará 404. Tras los cambios, recarga Nginx y vuelve a probar.

Tarea 7: Confirma que WordPress piensa que la URL del sitio coincide con la realidad

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

Qué significa la salida: Estas URLs controlan redirecciones canónicas y cookies. Un desajuste (http vs https, www vs sin-www) rompe la autenticación por nonce/cookie y crea bucles de redirección.

Decisión: Si están incorrectas, arréglalas (con cuidado) y luego valida las redirecciones y las llamadas REST.

Tarea 8: Inspecciona las reglas de reescritura de WordPress y vacíalas con seguridad

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

Qué significa la salida: Las reglas de reescritura REST existen a nivel de WordPress.

Decisión: Si faltan, vacía las reglas:

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

Decisión: Si WordPress tiene las reglas pero HTTP sigue 404, tu servidor web no las está respetando (config Nginx, Apache AllowOverride, etc.).

Tarea 9: Comprueba si Apache permite overrides vía .htaccess

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>

Qué significa la salida: AllowOverride None significa que tu .htaccess se ignora. Los permalinks y las reescrituras REST fallan a menos que el vhost tenga reglas de reescritura equivalentes.

Decisión: Establece AllowOverride All (o añade explícitamente las reglas de reescritura en el vhost), recarga Apache y vuelve a probar /wp-json/.

Tarea 10: Identifica si un plugin está interceptando solicitudes REST

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   |
+-------------------------+--------+-----------+---------+

Qué significa la salida: Plugins de seguridad y de caché son culpables comunes de problemas con REST. No es para culpar; es probabilístico.

Decisión: Desactiva temporalmente los plugins sospechosos (en una ventana controlada) y vuelve a probar. Empieza por plugins de WAF/seguridad y plugins de caché agresivos.

Tarea 11: Desactiva plugins temporalmente sin arruinar producción

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

Qué significa la salida: Estás probando si la REST se recupera al eliminar la intercepción.

Decisión: Si REST se recupera, vuelve a activar un plugin a la vez para aislar. Luego configura: permite /wp-json/, desactiva el bloqueo REST, excluye la caché para endpoints REST.

Tarea 12: Busca errores relacionados con REST en los logs de PHP

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"

Qué significa la salida: Ese fatal puede convertir una petición API en un 500 y WordPress mostrará “REST API encountered an error.”

Decisión: Revertir la versión del plugin/tema, parchear el código o fijar la versión de PHP si se introdujo una incompatibilidad. Arreglar el fatal es innegociable.

Tarea 13: Inspecciona los logs de acceso de Nginx/Apache para estado y tiempos upstream

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"

Qué significa la salida: El GET público funciona, el POST autenticado falla con 401, pero una petición autenticada vía rest_route funciona (quizá con cookies). Esto apunta a diferencias en el flujo de auth, cabeceras eliminadas o problemas con nonces.

Decisión: Enfócate en la autenticación: cookies, nonces, Application Passwords, cabeceras del proxy o plugins de seguridad que traten los POSTs a /wp-json de forma distinta.

Tarea 14: Confirma que las cabeceras de respuesta no son eliminadas por el 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

Qué significa la salida: Para muchos endpoints REST, el comportamiento correcto de cache-control importa. La ausencia de Set-Cookie en flujos de login o de WWW-Authenticate en 401 también puede señalar manipulación de cabeceras.

Decisión: Si las cabeceras difieren entre origen y borde, arregla la configuración del proxy (paso de cabeceras) o el comportamiento del CDN. REST y caché “inteligente” no son amigos por defecto.

Tarea 15: Comprueba si la autenticación básica funciona con una 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\/"}

Qué significa la salida: La autenticación funciona y WordPress puede servir solicitudes REST autenticadas.

Decisión: Si el editor sigue fallando pero esto funciona, tu problema probablemente sea cookie+nonce o CORS, no “REST está caída”.

Tarea 16: Detecta bucles de redirección y peleas de canonicalización

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

Qué significa la salida: HTTP fue redirigido a HTTPS y www, y luego tuvo éxito.

Decisión: Si ves redirecciones repetidas o termina en una ubicación no JSON, arregla siteurl/home, cabeceras del proxy (X-Forwarded-Proto) y reglas canónicas en el borde y WordPress.

Tarea 17: Comprueba cabeceras CORS para frontends headless

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

Qué significa la salida: El preflight OPTIONS está siendo bloqueado. Muchos WAFs tratan OPTIONS como sospechoso. Los navegadores rechazarán la petición real si el preflight falla.

Decisión: Permite OPTIONS a /wp-json/* en el borde, y configura WordPress/plugin de CORS cuidadosamente. También confirma que no estés requiriendo auth para el preflight por error.

Tarea 18: Comprueba la saturación de PHP-FPM (REST “funciona” hasta que deja de hacerlo)

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"

Qué significa la salida: Ceros trabajadores inactivos y solicitudes lentas en aumento indican que la REST puede fallar bajo carga con 502/504, especialmente en endpoints autenticados.

Decisión: Incrementa la capacidad FPM (dentro de límites de RAM), reduce plugins costosos, añade caché para endpoints públicos y evita que bots golpeen /wp-json.

Qué suele romper REST (modos reales de fallo)

1) Reescrituras y permalinks: la fundación aburrida

Si /wp-json/ devuelve 404 pero index.php?rest_route=/ funciona, tus reescrituras están rotas. Normalmente es una de:

  • mod_rewrite de Apache deshabilitado.
  • AllowOverride None que bloquea .htaccess.
  • Falta en Nginx del fallback try_files ... /index.php.
  • Reglas de multisite que no coinciden con la estructura real de directorios.
  • Permalinks alternados y reglas no vaciadas.

Orientación con opinión: si usas Nginx y dependes de “reescrituras automáticas” de WordPress, ya estás negociando con la física. Pon las reglas correctas de Nginx en control de versiones y deja de fingir que la UI de administración controla tu servidor web.

2) Proxies inversos y redirecciones “útiles”

REST es sensible a la corrección de esquema/host porque la autenticación usa cookies y nonces ligados al origen. Si tu proxy termina TLS pero WordPress piensa que está en HTTP, verás:

  • bucles de redirección
  • cookies seteadas para el dominio equivocado
  • fallos de validación de nonce (a menudo mostrando 401)

Los arreglos suelen implicar establecer las cabeceras forward correctas y asegurarse de que WordPress las respete (o establecer home/siteurl correctamente, además de configuración consciente del proxy).

3) WAF y protección anti-bots que bloquean /wp-json

Las capas de seguridad adoran bloquear APIs JSON porque los atacantes las usan. Desafortunadamente, tu editor también ama las APIs JSON.

Disparadores comunes del WAF:

  • Cuerpos POST que parecen HTML o contienen <script> en contenido legítimo.
  • Solicitudes repetidas desde usuarios autenticados (Gutenberg es muy chatty).
  • Preflight OPTIONS para CORS.
  • Limitación de tasa que no distingue entre bots y editores.

Arregla estrechando excepciones: permite métodos y paths específicos, registra con agresividad y mantiene el radio de explosión pequeño. “Deshabilitar WAF” no es una solución; es una carta de renuncia.

4) Capas de caché que tratan REST como contenido estático

Los endpoints públicos pueden cachearse con cuidado. Los endpoints autenticados normalmente no deberían cachearse en el borde. Cuando las cachés fallan en esto, ves:

  • usuarios viendo datos de otros usuarios (raro pero catastrófico)
  • nonces obsoletos y 401 aleatorios
  • respuestas HTML aleatorias porque la caché guardó una página de bloqueo

Regla práctica: nunca caches solicitudes con cookies en el borde a menos que sepas exactamente lo que haces y puedas demostrar aislamiento seguro.

5) Conflictos de plugins: registro de rutas REST y filtrado de solicitudes

Los plugins pueden romper REST al:

  • registrar rutas incorrectamente (conflictos de namespace, callbacks de permiso erróneos)
  • filtrar rest_authentication_errors y devolver errores incondicionalmente
  • imprimir espacios en blanco/notificaciones que corrompen JSON
  • cambiar content-type o hacer buffer de salida

Si desactivar un plugin lo arregla, encontraste al culpable. Si desactivar todos los plugins lo arregla, encontraste un plugin, pero no cuál. Aísla sistemáticamente.

6) Autenticación: cookies, nonces y las muchas maneras en que se estropean

Gutenberg y las pantallas de administración a menudo usan autenticación por cookie con nonces. Eso significa que el navegador debe:

  • enviar las cookies correctas para el dominio/path correcto
  • enviar una cabecera de nonce válida (X-WP-Nonce)
  • no estar bloqueado por políticas SameSite o desajustes entre dominios

Cuando esto falla, la REST puede estar bien, pero el editor se quejará. Lo arreglas alineando dominios, esquemas y comportamiento de cookies—no “reinstalando WordPress”.

7) Fiabilidad del backend: fatales de PHP, timeouts de BD, problemas de almacenamiento

Las solicitudes REST ejecutan rutas de código de WordPress. Tocan la base de datos. Leen/escriben opciones. Pueden generar thumbnails. Pueden llamar APIs externas. Si tu almacenamiento es lento o la BD está mal, los endpoints REST fallarán primero porque son invocados constantemente por las funciones modernas de WordPress.

Nota de ingeniero de almacenamiento: las fallas “aleatorias” de REST bajo carga suelen correlacionar con picos de latencia I/O, especialmente en hosting compartido o discos en red subdimensionados. Revisa I/O wait y la salud de la BD antes de acusar a REST de “inestable”.

Broma #2: Si tu REST solo falla los lunes, no está embrujada—es tu calendario de despliegues con máscara.

Tres historias corporativas desde el frente

Mini-historia 1: El incidente causado por una suposición equivocada

Una compañía mediana ejecutaba WordPress detrás de un proxy inverso y un CDN. Desplegaron una nueva landing headless que tiraba posts vía REST. En staging funcionó. En producción, el editor empezó a fallar al publicar y la página headless a veces devolvía 401. Todos culparon a “WordPress como siempre”.

La suposición equivocada fue simple: “Si la web carga, la API debe estar bien.” La página principal estaba cacheada en el CDN. Las llamadas REST no—salvo que a veces sí, porque el CDN tenía una regla genérica que cacheaba “todas las solicitudes GET”. Los GET públicos de REST se cacheaban. Los GET autenticados también, porque la clave de caché ignoraba cookies por rendimiento.

El resultado fue caos intermitente: la respuesta autenticada de un editor podía servirse a otro (a veces bloqueada por comprobaciones de capacidad, a veces no). El equipo no notó fuga de datos, pero sí sesiones rotas y fallos de nonce porque las respuestas cacheadas no coincidían con las expectativas del navegador.

La solución no fue heroica. Cambiaron la política del CDN: omitir la caché para cualquier solicitud con Cookie y para cualquier ruta bajo /wp-json/ salvo que se haya permitido explícitamente. Luego permitieron unas pocas rutas públicas con una clave de caché segura y TTL corto. REST se estabilizó al instante.

Conclusión: nunca asumas que “la web funciona” implica “la API funciona”, especialmente cuando hay caché. Tu homepage es mentirosa; curl es honesto.

Mini-historia 2: La optimización que salió mal

Un equipo de marketing empresarial quería TTFB más rápido. Un ingeniero activó optimizaciones HTML agresivas y compresión de respuesta en el proxy inverso. También activó un conjunto de reglas de “endurecimiento de seguridad” para bloquear “query strings sospechosos”.

El rendimiento mejoró para el sitio público. Luego Gutenberg empezó a fallar en subidas de imágenes y actualizaciones de posts. El panel de Site Health reportó errores de la REST API. Los desarrolladores comenzaron a perseguir supuestos conflictos de plugins.

El problema real: la optimización del proxy incluía una regla que bufferizaba y reescribía respuestas y aplicaba un pequeño client_max_body_size. Las subidas de media vía REST chocaban con 413. Mientras tanto, la regla de “query string sospechoso” marcaba patrones con rest_route= y bloqueaba solicitudes de fallback usadas por algunos clientes y plugins.

La corrección fue embarazosamente mundana: aumentar límites de cuerpo en los bloques de location relevantes, desactivar la reescritura para respuestas JSON y ajustar reglas WAF para rutas REST conocidas. También añadieron monitorización específica para códigos de estado de /wp-json/ para detectar regresiones antes de que marketing las notara.

Conclusión: las optimizaciones están permitidas. Pero necesitan una mentalidad de lista blanca para APIs. Si tu proxy modifica payloads, estás haciendo trabajo de aplicación sin pruebas de aplicación.

Mini-historia 3: La práctica aburrida pero correcta que salvó el día

Una organización global ejecutaba múltiples instancias de WordPress con infraestructura como código idéntica para Nginx y PHP-FPM. Su equipo SRE impuso una política aburrida: cada cambio de configuración tenía un canario, y cada canario tenía una comprobación sintética que llamaba a /wp-json/, a un endpoint público y a uno autenticado.

Una semana, un cambio menor de Nginx destinado a endurecer la seguridad eliminó accidentalmente el fallback try_files y convirtió paths desconocidos en 404. El sitio público siguió funcionando porque la mayoría de páginas estaban cacheadas y las rutas comunes existían. Pero las llamadas REST empezaron a fallar de inmediato en el canario.

Las comprobaciones sintéticas lo detectaron en minutos. Revirtieron antes del despliegue completo. Ningún aluvión de tickets de clientes. Solo un revert limpio y un pequeño postmortem.

Conclusión: las prácticas “aburridas”—canarios, comprobaciones sintéticas y configuración en control de versiones—te salvan de la astucia. REST es un canario perfecto porque ejerce routing, PHP y lógica de app rápidamente.

Errores comunes: síntoma → causa raíz → solución

Esta sección está pensada para usarse bajo presión. Encuentra tu síntoma y aplica la solución. No debatas filosofía durante un outage.

1) WordPress dice “REST API encountered an error,” y /wp-json devuelve HTML

  • Síntoma: content-type: text/html, quizá una página de login o de bloqueo.
  • Causa raíz: WAF/CDN bloqueando, redirección a login o plantilla de error cacheada.
  • Solución: Evita el borde y compara con el origen. Desactiva caché para /wp-json. Añade reglas allow en el WAF para rutas/métodos REST. Confirma que no se aplique redirect de auth a las rutas API.

2) /wp-json funciona, pero endpoints autenticados devuelven 401 en el editor

  • Síntoma: Endpoints públicos OK; acciones del editor fallan; “nonce inválido” aleatorio.
  • Causa raíz: Mismatch de dominio/esquema en cookies, problemas SameSite, proxy que no envía X-Forwarded-Proto correcto, o caché de nonces.
  • Solución: Alinea home y siteurl. Arregla cabeceras del proxy y redirecciones canónicas. Asegura que solicitudes autenticadas no estén cacheadas en ningún lado.

3) /wp-json devuelve 404 pero index.php?rest_route funciona

  • Síntoma: El path de reescritura falla, la consulta directa funciona.
  • Causa raíz: Misconfiguración de reescrituras en Nginx/Apache, .htaccess ignorado o falta de try_files fallback.
  • Solución: Arregla la config del servidor. Vacía las reescrituras después. Vuelve a probar ambos endpoints hasta que se comporten igual.

4) Solicitudes REST devuelven 403 solo para POST/PUT/DELETE

  • Síntoma: GET funciona; escrituras están bloqueadas.
  • Causa raíz: Regla WAF que bloquea métodos, falsos positivos del mod_security CRS, o restricciones de método en el proxy.
  • Solución: Revisa logs del WAF por ID de regla y ajusta excepciones de forma estrecha para /wp-json. Asegura que el proxy permita esos métodos al origen.

5) REST devuelve 500 intermitentemente, logs muestran fatales de PHP

  • Síntoma: 500/502 con stack traces en logs de PHP.
  • Causa raíz: Bug en plugin/tema o incompatibilidad de versión de PHP.
  • Solución: Revierte el cambio que introdujo fatales. Parchea el plugin. Añade tests automatizados o al menos comprobaciones sintéticas REST en pipelines de despliegue.

6) REST es lenta; a veces 504 Gateway Timeout

  • Síntoma: Tiempos de respuesta largos y luego timeouts bajo carga.
  • Causa raíz: Agotamiento de workers de PHP-FPM, contención de BD, almacenamiento lento, llamadas externas en la ruta de la solicitud.
  • Solución: Perfila solicitudes lentas; aumenta capacidad FPM con cuidado; optimiza BD y object caching; añade timeouts/circuit breakers para llamadas externas; bloquea clientes abusivos.

7) Errores CORS en consola del navegador, curl funciona

  • Síntoma: El navegador bloquea solicitudes; curl tiene éxito.
  • Causa raíz: Cabeceras CORS faltantes/bloqueadas, OPTIONS bloqueado, cabeceras de origen incorrectas.
  • Solución: Permite OPTIONS a través del WAF/proxy y configura Access-Control-Allow-Origin y cabeceras relacionadas correctamente, idealmente con una lista blanca.

Listas de verificación / plan paso a paso

Checklist A: Tienes un error REST ahora mismo

  1. Ejecuta curl a /wp-json/ y captura estado, cabeceras y tipo de cuerpo (JSON vs HTML).
  2. Ejecuta curl a /index.php?rest_route=/ y compara.
  3. Prueba desde el host de origen con la cabecera Host: para evitar el borde.
  4. Si el borde está implicado: revisa logs WAF/CDN por bloqueos, límites de tasa y restricciones de método.
  5. Si las reescrituras están implicadas: inspecciona try_files en Nginx o AllowOverride/mod_rewrite en Apache.
  6. Si la auth está implicada: prueba Application Password; luego prueba flujos editor/cookie+nonce.
  7. Si hay 500/timeout: revisa logs de PHP, saturación FPM, salud de BD y cambios recientes de deploy/plugin.

Checklist B: Evita el próximo incidente (higiene operacional)

  1. Añade comprobaciones sintéticas para /wp-json/, un endpoint público y uno autenticado.
  2. Instrumenta códigos de estado para /wp-json en logs de acceso y alerta por picos de 4xx/5xx.
  3. Pon las configs de Nginx/Apache en control de versiones; valida cambios con un canario.
  4. Define la política de caché explícitamente: qué endpoints REST son cacheables y cómo se construyen las claves de caché.
  5. Crea un proceso de excepciones para WAF: listas blancas por path/método, seguimiento de ID de regla y revisiones de expiración.
  6. Mantén actualizaciones de plugins en staging; monitoriza comprobaciones sintéticas REST después del deploy.

Checklist C: Cuando sospechas un conflicto de plugin

  1. Toma un snapshot de la lista de plugins activos.
  2. Desactiva un plugin de alto riesgo (seguridad/caché) a la vez.
  3. Vuelve a probar /wp-json/ y la ruta que falla.
  4. Cuando funcione, vuelve a activar los demás uno por uno para aislar.
  5. Aplica una corrección de configuración o reemplaza el plugin si no puede coexistir con tus requisitos.

Datos e historia interesantes (breve, contexto útil)

  • Dato 1: La REST API de WordPress empezó como un plugin de características antes de incorporarse al core, por eso posts antiguos aún mencionan el “plugin de REST API”.
  • Dato 2: El endpoint base /wp-json/ es un documento índice—una “tabla de contenidos” de la API—por lo que un índice roto suele ser un problema de enrutamiento o bloqueo, no de “posts faltantes”.
  • Dato 3: Gutenberg (el editor de bloques) depende mucho de REST. Si REST está inestable, el editor suele ser el primero en quejarse.
  • Dato 4: WordPress puede servir REST vía el parámetro de consulta (rest_route), lo que lo convierte en una gran herramienta para distinguir problemas de reescritura de problemas de aplicación.
  • Dato 5: Muchos productos de seguridad históricamente trataron endpoints JSON como “superficies API” y por defecto aplicaron inspección más estricta, por eso los falsos positivos se agrupan alrededor de /wp-json.
  • Dato 6: Las rutas REST se registran por código en tiempo de ejecución. Si un plugin no carga (fatal), namespaces enteros pueden desaparecer, convirtiendo 200 esperados en 404.
  • Dato 7: La autenticación de WordPress para llamadas REST basadas en navegador suele usar nonces en lugar de Basic Auth; los nonces tienen tiempo limitado y se rompen fácilmente con caché o desajuste de reloj.
  • Dato 8: Históricamente, algunos hosts y plugins bloquearon rutas /wp-json/wp/v2/users para reducir riesgo de enumeración, a veces bloqueando demasiado y rompiendo acciones administrativas legítimas.
  • Dato 9: Políticas de caché de CDN que ignoran cookies pueden causar que respuestas API se filtren entre sesiones—raro, pero ha pasado lo suficiente como para que SREs veteranos se pongan nerviosos.

Preguntas frecuentes

1) ¿Cómo sé si la REST de WordPress está caída o solo es el editor?

Ejecuta curl -D- https://example.com/wp-json/. Si devuelve 200 con JSON, REST está arriba. Si el editor falla, céntrate en auth/nonce/cookies y caché.

2) ¿Por qué /wp-json funciona pero /wp-json/wp/v2/posts falla?

El índice puede cargar mientras rutas específicas fallan por código de plugin/tema, callbacks de permiso o problemas de registro de rutas. Revisa logs de PHP y desactiva plugins para aislar.

3) ¿Cuál es la prueba de reescritura más rápida?

Compara /wp-json/ con /index.php?rest_route=/. Si solo funciona la segunda, tu servidor web tiene problemas de reescritura.

4) ¿Por qué recibo 401 Unauthorized incluso estando logueado en wp-admin?

Típicamente mismatch cookie/nonce por inconsistencia de dominio o esquema (www vs sin-www, http vs https), comportamiento SameSite de cookies o un proxy que no reporta HTTPS correctamente.

5) ¿Cómo sé si el WAF está bloqueando REST?

Busca 403 con páginas HTML de bloqueo, cabeceras de CDN/WAF o bloqueos que ocurren solo en POST/OPTIONS. Evita el borde y prueba el origen; si el origen funciona, es WAF/CDN.

6) ¿Puedo cachear respuestas de la REST API?

Sí, de forma selectiva. Cachea solo endpoints verdaderamente públicos y no personalizados. Nunca caches solicitudes con cookies en el borde a menos que puedas demostrar claves de caché seguras y aislamiento.

7) ¿Por qué veo errores CORS pero curl funciona?

Los navegadores aplican CORS y suelen enviar un preflight OPTIONS. Si OPTIONS está bloqueado o cabeceras CORS no son correctas, el navegador rechaza. Curl no aplica esa restricción.

8) ¿Desactivar plugins siempre prueba que es un problema de plugin?

Prueba interacción, no culpabilidad. Un plugin puede sólo disparar una regla del proxy/WAF o exponer un problema latente de configuración. Úsalo para acotar el alcance y luego valida la causa raíz real.

9) ¿Cuál es el método de autenticación más seguro para llamadas REST servidor a servidor?

Application Passwords suelen ser lo menos doloroso en WordPress core. Usa HTTPS, restringe el alcance, rota credenciales y monitoriza abuso.

10) ¿Por qué REST falla solo bajo carga?

Porque bajo carga alcanzas límites: workers de PHP-FPM, conexiones de BD, latencia I/O o limitaciones de tasa. Los endpoints REST son chatty y amplifican los cuellos de botella rápido.

Conclusión: siguientes pasos que realmente reducen recurrencia

Si solo vas a tomar unas pocas acciones de esto, haz estas:

  1. Haz que /wp-json/ sea observable. Añade comprobaciones sintéticas y alertas sobre códigos de estado y latencia. REST es el canario para WordPress moderno.
  2. Separa enrutamiento, auth y salud del backend. Usa la prueba rest_route para probar reescrituras, luego prueba endpoints públicos y después los autenticados.
  3. Deja de permitir que las cajas intermedias improvisen. Define explícitamente comportamiento de caché y WAF para /wp-json. El default-deny está bien; las sorpresas por defecto no lo están.
  4. Controla en versión la configuración del servidor web. Si la disponibilidad de REST depende de un snippet de Nginx pegado hace tres años, no tienes config—tienes folklore.

Ahora haz lo poco glamuroso: ejecuta las primeras tres tareas, anota el comportamiento observado y sigue la capa hacia donde la realidad apunte. Las fallas de la REST de WordPress rara vez son misteriosas. Simplemente están distribuidas en suficientes componentes como para que la gente confunda “complicado” con “inexplicable”.

← Anterior
VPN de oficina: permitir acceso a un solo servidor/puerto (principio de menor privilegio)
Siguiente →
Configuración de caché Cloudflare para WordPress que no rompe wp-admin

Deja un comentario