WP Headless Mode v1.3.1

Plugin para WordPress que convierte una instalación estándar en un CMS headless optimizado. Requiere PHP 8.0+ y WordPress 6.0+.

¿Qué hace este plugin?

WP Headless Mode transforma WordPress en un backend API-first, desacoplando completamente el frontend del servidor. Al activarlo:

Instalación

  1. Copia la carpeta wp-headless-mode a /wp-content/plugins/
  2. Activa el plugin desde Plugins → Plugins instalados
  3. Ve a Headless Mode → Configuración y activa el modo headless
  4. Configura los orígenes CORS permitidos con la URL de tu frontend

Arquitectura

wp-headless-mode/ ├── wp-headless-mode.php ← Entry point, bootstrap, activation hooks ├── includes/ │ ├── class-headless-core.php ← Bloqueo frontend, features │ ├── class-headless-optimizer.php ← Plugins filter, heartbeat, autoload │ ├── class-plugin-classifier.php ← Clasificación de plugins │ ├── class-api-enhancer.php ← CORS, rate limit, security headers │ └── templates/ │ └── headless-frontend.php ← Template JSON para frontend bloqueado ├── admin/ │ ├── class-headless-admin.php ← Menú admin, AJAX, badges │ └── views/ │ ├── dashboard.php │ ├── plugins.php │ ├── settings.php │ └── themes.php ├── theme/ │ ├── style.css ← Placeholder theme header │ └── index.php └── assets/ ├── admin.css └── admin.js

Flujo de arranque

plugins_loaded
  └── wp_headless_mode_init()
        ├── WP_Headless_Core::init()            → hooks de template y features
        ├── WP_Headless_Optimizer::init()       → hooks de plugins y cron
        ├── WP_Headless_API_Enhancer::init()    → hooks REST
        └── WP_Headless_Admin::init()           → (solo is_admin()) menú y AJAX

Módulo: Headless Core

Archivo: includes/class-headless-core.php

Responsable de bloquear el frontend y eliminar ruido de WordPress.

AcciónHookDescripción
Bloquea frontendtemplate_include (PHP_INT_MAX)Devuelve headless-frontend.php → JSON 404
Vacía assets del themewp_enqueue_scripts (PHP_INT_MAX)Elimina todos los scripts y estilos en frontend
Desactiva XML-RPCxmlrpc_enabledStub que responde 403 JSON
Desactiva embedsinitElimina rutas, filters y head links de oEmbed
Desactiva emojisinitElimina emoji.js y DNS prefetch de s.w.org
Limpia <head>initElimina generator, shortlink, feed links, REST link
Añade headersend_headersX-WP-Headless: 1 en todas las respuestas

Respuesta a requests de frontend bloqueados

{
  "error":   "not_found",
  "message": "This WordPress installation operates in headless mode...",
  "api": {
    "rest":   "https://tu-sitio.com/wp-json/wp/v2/",
    "health": "https://tu-sitio.com/wp-json/headless/v1/health"
  }
}

Módulo: Optimizer

Archivo: includes/class-headless-optimizer.php

Filtrado de plugins en REST

En cada request a la REST API, el Optimizer modifica la opción active_plugins en memoria para excluir plugins de frontend. Plugins como Elementor, WP Rocket o Jetpack nunca llegan a cargar en requests de API, ahorrando entre 8–30 MB de RAM por request.

// Plugins excluidos automáticamente en requests REST:
elementor, elementor-pro, beaver-builder-plugin,
wp-super-cache, w3-total-cache, wp-rocket,
litespeed-cache, autoptimize, wp-fastest-cache, jetpack
Cómo funciona Los plugins marcados como frontend en los overrides manuales también se excluyen. El filtro actúa sobre option_active_plugins con prioridad 1, antes de que los plugins registren sus hooks.

Módulo: Plugin Classifier

Archivo: includes/class-plugin-classifier.php

ClasificaciónSignificadoImpacto
headless-readyDiseñado para headless (WPGraphQL, ACF, JWT...)Ninguno
admin-onlySolo actúa en wp-admin (Wordfence, UpdraftPlus...)Ninguno
api-awareTiene REST API pero también carga assets en frontendBajo
frontendRenderizador HTML, incompatible con headlessAlto
unknownSin clasificación conocida, requiere revisión manualMedio

Módulo: API Enhancer

Archivo: includes/class-api-enhancer.php

Headers de seguridad en todas las respuestas REST

X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cache-Control: no-store, no-cache, must-revalidate
X-Headless-Mode: true
X-Response-Time: 42ms

Módulo: Admin Panel

Archivo: admin/class-headless-admin.php

SecciónURLDescripción
Dashboard?page=wp-headless-modeEstado, stats, endpoints activos, optimización DB
Plugins?page=wp-headless-pluginsTabla filtrable con clasificación y override manual
Configuración?page=wp-headless-settingsTodos los toggles y configuración
Theme?page=wp-headless-themeInstalar placeholder theme y limpiar themes innecesarios

Eliminación del menú Apariencia

Cuando el modo headless está activo, el menú Apariencia de WordPress se elimina automáticamente del panel de administración. Esto incluye todos sus submenús: Themes, Customizer, Widgets, Menús y Theme Editor.

Comportamiento dinámico Si desactivas el modo headless desde Configuración, el menú Apariencia vuelve a aparecer automáticamente en la siguiente carga del admin.

Gestión del theme placeholder

WordPress requiere un theme activo internamente aunque el frontend esté bloqueado. La sección Theme del menú permite instalar el placeholder mínimo (2 archivos) y eliminar todos los themes innecesarios.


Referencia: Audit API

Base: /wp-json/headless/v1/ — Requieren usuario con capacidad manage_options.

GET /plugins Lista de plugins clasificados

Parámetros opcionales: classification (filtro por tipo) · active_only=true

POST /plugins/{slug}/classify Sobrescribir clasificación
{ "classification": "admin-only" }
GET /health Health check público
{
  "status":      "ok",
  "headless":    true,
  "version":     "1.3.1",
  "wp_version":  "6.7",
  "php_version": "8.2.0",
  "timestamp":   "2026-03-31T12:00:00+00:00"
}
GET /settings Configuración actual
PATCH /settings Actualizar configuración

Autenticación

Cuando Requerir auth en REST API está activo, todos los endpoints requieren autenticación. WordPress soporta nativamente Application Passwords (desde WP 5.6):

# Con Application Password (usuario:app-password en base64)
curl https://tu-sitio.com/wp-json/wp/v2/posts \
  -H "Authorization: Basic dXN1YXJpbzpwYXNz"
Recomendación Genera un Application Password en Usuarios → Tu perfil → Application Passwords.

Rate Limiting

X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Window: 3600

CORS

Configura los orígenes desde Configuración → Orígenes CORS permitidos. Un origen por línea. Vacío = acepta cualquier origen (*).

Producción Restringe siempre los orígenes en entornos productivos para evitar accesos no autorizados.

Base de datos

wp_headless_ratelimit

ip            VARCHAR(45)
endpoint      VARCHAR(255)
hits          INT
window_start  DATETIME      INDEX
PRIMARY KEY (ip, endpoint)

Settings

Guardadas en wp_options bajo la clave wp_headless_settings.

headless_mode
bool · default: true
Master switch del plugin
disable_themes
bool · default: true
Bloquea frontend → JSON 404
disable_xmlrpc
bool · default: true
Desactiva XML-RPC completamente
disable_embeds
bool · default: true
Elimina oEmbed y rutas de embeds
disable_emojis
bool · default: true
Elimina emoji.js del frontend y admin
disable_rest_open
bool · default: false
Requiere auth en todos los endpoints REST
api_rate_limit
bool · default: true
Activa rate limiting
api_rate_limit_max
int · default: 100
Requests/hora para usuarios anónimos
cors_origins
string · default: ''
Orígenes permitidos, uno por línea
jwt_auth
bool · default: false
JWT Auth (pendiente)

Auditoría de plugins

Para sobrescribir una clasificación usa el dropdown en Plugins o la API:

curl -X POST https://tu-sitio.com/wp-json/headless/v1/plugins/elementor/classify \
  -H "Cookie: wordpress_logged_in_..." \
  -d '{"classification": "frontend"}'

Optimización de autoload

Marca como autoload=no los siguientes patrones de wp_options:

widget_%         → Configuraciones de widgets
sidebars_widgets → Sidebar configuration
%_transient_%    → Transients
elementor%       → Opciones de Elementor
fl_%             → Opciones de Beaver Builder
theme_mods_%     → Modificaciones del theme activo
Nota Esta operación no se revierte automáticamente. Si vuelves al frontend de WordPress, puede ser necesario revertirla manualmente en wp_options.

Limitaciones conocidas

El bootstrap de WordPress siempre cargará ~200 archivos PHP y ejecutará la query de wp_options antes de que cualquier plugin pueda intervenir. Para reducir este overhead:

Plugins de frontend con CPTs Si un plugin clasificado como frontend registra Custom Post Types o meta fields que necesitas en la API, excluirlo en REST lo romperá. Verifica qué CPTs registra antes de marcarlo como frontend.

Acerca del autor

Javier Rivera es Full Stack Developer con más de 25 años de experiencia construyendo soluciones digitales, habiendo evolucionado desde programador hasta liderazgo técnico y arquitectura de software.

Especializado en arquitecturas headless, e-commerce (WordPress, WooCommerce, Shopify, Magento), integraciones empresariales y desarrollo de APIs. Ha trabajado en proyectos en más de 6 países para clientes en educación, farmacéutica, retail, finanzas y gobierno.

ÁreaTecnologías
BackendPHP · Node.js · Python · FastAPI
FrontendJavaScript · Next.js
InfraestructuraDocker · AWS · Azure · DigitalOcean
HerramientasClaude · GitHub Copilot · n8n

Más información en jjrc.dev  ·  hola@jjrc.dev