headless-wordpress-astro-setup.html
< BACK Escritorio vintage con páginas manuscritas y grabadora en luz dorada de atardecer

WordPress sin cabecera + Astro: Una configuración funcional para sitios con mucho contenido

Un cliente me llamó a principios de 2023 — un editor de medios ejecutando alrededor de 14,000 posts en WordPress, un tema armado entre cuatro desarrolladores durante seis años, y una puntuación de Core Web Vitals que era genuinamente vergonzosa. Su LCP estaba en 7.2 segundos en móvil. Habían intentado WP Rocket, probaron un CDN, incluso eliminaron la mitad de sus plugins. Aún así lento. El problema no era WordPress en sí. Era que cada renderizado de página pasaba por PHP, un tema inflado, y una cadena de consultas a la base de datos que no se tocaba desde 2018.

Ese fue el momento en que me comprometí de verdad con una configuración sin cabecera usando Astro como frontend. No porque sea moderno — porque para un sitio con miles de posts y contenido editorial pesado, era la única arquitectura que tenía sentido.Astro as the frontend. Not because it's fashionable — because for a site pushing thousands of posts with heavy editorial content, it was the only architecture that made sense.

Aquí está exactamente cómo lo configuré. Los compromisos, la configuración real, las partes que me causaron problemas.

---

¿Por qué Astro y no Next.js?

Me hacen esta pregunta seguido. Next.js es la respuesta obvia si ya tienes un equipo pesado en React o necesitas interactividad compleja del lado del cliente. Pero ¿para sitios con mucho contenido? Astro gana, y no es particularmente cercano.

Astro envía cero JavaScript por defecto. Para un blog, un sitio de noticias o un portal de documentación, ese es el default correcto. Optas por JavaScript donde lo necesitas, en lugar de optar por no usar un bundle React de 200kb que mayormente no quieres. Los docs de Astro sobre hidratación parcial —la llaman Islands architecture— lo explican mejor que yo en una frase, pero la versión corta es: solo los bits interactivos obtienen JS. El cuerpo del artículo, el header, la barra lateral. HTML estático.zero JavaScript by default. For a blog, a news site, or a documentation portal, that's the correct default. You opt into JavaScript where you need it, rather than opting out of a 200kb React bundle you mostly don't want. The Astro docs on partial hydration — they call it the Islands architecture — explain it better than I can in a sentence, but the short version is: only the interactive bits get JS. The article body, the header, the sidebar? Static HTML.

Construí un sitio de contenido legal a fines de 2022 con Next.js y WordPress. Lo suficientemente rápido, pero el cliente seguía preguntando por qué su puntuación Lighthouse era 74 en mobile cuando "se supone que ahora es rápido". Overhead de hidratación. Con Astro, ese mismo tipo de sitio ahora rutinariamente llega a 95–98. No es alarde —es solo lo que la arquitectura te da gratis.

Lo que Astro No Hace Bien

Vale ser honesto. Si tu sitio necesita personalización en tiempo real, un carrito de compras pesado, o cualquier cosa que realmente dependa del estado del cliente en muchos componentes, Astro empieza a sentirse incómodo. No es una app React. El patrón Islands es poderoso, pero es un modelo mental diferente que construir SPAs. Intenté forzar un dashboard del cliente en un proyecto Astro a mediados de 2023 y terminé revirtiendo a Next.js en dos semanas. Sabe qué estás construyendo.

---

Configurando WordPress como un Headless CMS

WordPress es genuinamente un buen backend headless. El WP REST API viene en core, está bien documentado, y tu equipo editorial no tiene que aprender nada nuevo. Ese último punto importa más de lo que los desarrolladores generalmente admiten.WP REST API ships in core, it's well-documented, and your editorial team doesn't have to learn anything new. That last point matters more than developers usually admit.

Aquí está la configuración que uso:

  1. Instala WordPress en un subdominio —uso cms.tudominio.com o api.tudominio.com. Mantenlo detrás de autenticación básica o como mínimo restringe el tráfico público directo. El frontend es tudominio.com. Dos deployments separados. — I use cms.yourdomain.com or api.yourdomain.com. Keep it behind basic auth or at minimum restrict direct public traffic. The frontend is yourdomain.com. Two separate deployments.
  2. Instala el [plugin WPGraphQL](https://www.wpgraphql.com/) — prefiero GraphQL sobre REST para sitios de contenido porque puedes colocar tus queries junto a tus componentes y obtener exactamente los campos que necesitas. Sin sobre-fetching. La API REST funciona bien, pero una vez que tienes 15+ campos personalizados por tipo de post, el enfoque GraphQL es notoriamente más limpio. — I prefer GraphQL over REST for content sites because you can co-locate your queries with your components and fetch exactly the fields you need. No over-fetching. The REST API is fine, but once you've got 15+ custom fields per post type, the GraphQL approach is noticeably cleaner.
  3. Instala Advanced Custom Fields (ACF) y la extensión WPGraphQL para ACF. Esta combinación es lo que hace que WordPress sea genuinamente flexible como modelo de contenido headless — puedes definir datos estructurados por tipo de post, exponerlos a través de GraphQL, y Astro los consume de manera limpia. and the WPGraphQL for ACF extension. This combo is what makes WordPress genuinely flexible as a headless content model — you can define structured data per post type, expose it through GraphQL, and Astro consumes it cleanly.
  4. Desactiva comentarios, emojis y el XML-RPC predeterminado si aún no lo has hecho. Estos añaden sobrecarga y superficie de ataque que no necesitas. if you haven't already. These add overhead and attack surface you don't need.
  5. Establece los permalinks en algo sensato antes de empezar a construir. Cambiarlos a mitad del proyecto cuando tus rutas de Astro ya están configuradas es genuinamente problemático. to something sensible before you start building. Changing them mid-project when your Astro routes are already set is a genuine pain.

Una cosa que confunde a la gente: CORS. Por defecto, WordPress no permitirá que tu servidor de desarrollo de Astro (corriendo en localhost:4321) haga solicitudes a tu instalación de WP. Añade esto a tu functions.php del tema o a un pequeño plugin utilitario durante el desarrollo:localhost:4321) make requests to your WP install. Drop this into your theme's functions.php or a small utility plugin during development:

`` add_action('init', function() { header("Access-Control-Allow-Origin: *"); }); `` add_action('init', function() { header("Access-Control-Allow-Origin: *"); }); ``

Ajusta eso a orígenes específicos en producción. Obviamente.

---

La Estructura del Proyecto Astro

Mantengo esto con opinión y consistencia en todos los proyectos. Después de una docena o más de construcciones headless, esto es lo que funciona:

`` src/ components/ layouts/ pages/ index.astro blog/ [slug].astro lib/ wpgraphql.ts ← toda la lógica de consultas WP vive aquí styles/ `` src/ components/ layouts/ pages/ index.astro blog/ [slug].astro lib/ wpgraphql.ts ← all WP query logic lives here styles/ ``

El archivo lib/wpgraphql.ts es donde centralizo cada fetch de GraphQL. Sin llamadas fetch inline dispersas en archivos de páginas. Cada consulta es una función async nombrada y exportada. Debuggear esto en 14,000 posts cuando algo se rompe a las 2am — te lo agradecerás después.lib/wpgraphql.ts file is where I centralise every GraphQL fetch. No inline fetch calls scattered across page files. Every query is a named, exported async function. Debugging this across 14,000 posts when something breaks at 2am — you'll thank yourself later.

Obteniendo Posts en Tiempo de Compilación

getStaticPaths de Astro es tu pan de cada día aquí. Para un blog con miles de posts:getStaticPaths is your bread and butter here. For a blog with thousands of posts:

`` export async function getStaticPaths() { const posts = await getAllPostSlugs(); // llama a WPGraphQL return posts.map(post => ({ params: { slug: post.slug }, })); } `` export async function getStaticPaths() { const posts = await getAllPostSlugs(); // calls WPGraphQL return posts.map(post => ({ params: { slug: post.slug }, })); } ``

getAllPostSlugs pagina a través de WPGraphQL usando cursores after — la capa GraphQL de WordPress devuelve 100 posts por solicitud por defecto, así que para 14,000 posts estás haciendo 140 solicitudes en tiempo de compilación. Suena aterrador. En la práctica, en un servidor decente, la compilación completa se ejecuta en unos 4–5 minutos. Perfectamente aceptable para un sitio que se recompila algunas veces por día. paginates through WPGraphQL using after cursors — WordPress's GraphQL layer returns 100 posts per request by default, so for 14,000 posts you're making 140 requests at build time. That sounds scary. In practice, on a decent server, the full build runs in about 4–5 minutes. Perfectly acceptable for a site that rebuilds a few times per day.

---

Manejando Imágenes Sin Perder la Cabeza

Este es el punto que nadie habla lo suficiente. WordPress almacena URLs de imágenes que apuntan a tu subdominio de CMS. Cuando Astro compila estáticamente, esas imágenes aún viven en cms.tudominio.com — lo que significa que los navegadores de tus visitantes están descargando imágenes desde tu servidor WordPress, potencialmente saltándose tu CDN.cms.yourdomain.com — which means your visitors' browsers are fetching images from your WordPress server, potentially bypassing your CDN.

Algunas formas en que manejo esto:

  • Cloudflare frente a ambos dominios. La opción más simple. Proxy de yourdomain.com y cms.yourdomain.com a través de Cloudflare, configura caché agresivo en /wp-content/uploads/*, y estás más o menos bien. Simplest option. Proxy both yourdomain.com and cms.yourdomain.com through Cloudflare, configure aggressive caching on /wp-content/uploads/*, and you're mostly fine.
  • Usa un plugin de offload de medios. Me gusta WP Offload Media — mueve uploads a S3 (o almacenamiento compatible) y reescribe las URLs automáticamente. Este es el enfoque que uso para cualquier sitio que espera tráfico serio. Tu servidor WordPress deja de servir imágenes completamente. I like WP Offload Media — it moves uploads to S3 (or compatible storage) and rewrites URLs automatically. This is the approach I use for any site expecting serious traffic. Your WordPress server stops serving images entirely.
  • Componente Image de Astro. Para imágenes que controlas en tiempo de compilación (imágenes destacadas extraídas vía GraphQL), puedes pasar la URL remota al componente <Image> de Astro y optimizará, redimensionará y las servirá desde tu output de compilación. Funciona brillantemente. No funciona para imágenes incrustadas en HTML del cuerpo del post — eso requiere un pase diferente. For images you control at build time (featured images pulled via GraphQL), you can pass the remote URL into Astro's <Image> component and it'll optimise, resize, and serve them from your build output. Works brilliantly. Does not work for images embedded in post body HTML — that requires a different pass.

Seahawk tuvo un cliente de contenido de viajes el año pasado — alrededor de 8,000 posts, extremadamente pesados en imágenes, promedio de 12 imágenes por artículo. Su servidor WordPress se estaba viendo atacado puramente por solicitudes de imágenes incluso con la configuración headless. Pasar a S3 + CloudFront redujo su ancho de banda de origen en 94%. Genuinamente transformador para su factura de hosting.

---

Compilaciones Incrementales y el Problema de la Reconstrucción

Aquí hay un problema real con la generación estática a escala: tu editor publica una corrección de post a las 3pm y tiene que esperar 5 minutos por una reconstrucción completa. Eso no es aceptable en una sala de redacción.

Algunos enfoques que he usado:

Opción 1: Netlify o Vercel con ISR bajo demanda. Astro soporta renderizado del lado del servidor con adaptadores — puedes ejecutar Astro en modo híbrido donde la mayoría de páginas son estáticas pero rutas específicas se renderizan bajo demanda. Para un sitio de noticias, a menudo pre-renderizaré estáticamente los últimos 30 días de posts (alto tráfico, necesita velocidad) y configuraré páginas de archivo más antiguas para renderizarse del lado del servidor bajo demanda. Lo mejor de ambos mundos. Astro supports server-side rendering with adapters — you can run Astro in hybrid mode where most pages are static but specific routes are rendered on-demand. For a news site, I'll often statically pre-render the last 30 days of posts (high traffic, needs speed) and set older archive pages to server-render on demand. Best of both worlds.

Opción 2: Compilaciones parciales disparadas por webhook. WordPress dispara un webhook al guardar un post (fácil con el plugin WP Webhooks). Ese webhook golpea un hook de despliegue de Netlify o Vercel. La compilación se ejecuta, solo busca lo que cambió. No es realmente parcial — Astro aún reconstruye todo — pero si mantienes tu compilación rápida, 4 minutos es manejable. WordPress fires a webhook on post save (easy with the WP Webhooks plugin). That webhook hits a Netlify or Vercel deploy hook. The build runs, it fetches only what's changed. It's not truly partial — Astro still rebuilds everything — but if you keep your build fast, 4 minutes is workable.

Opción 3: Simplemente usa SSR para todo. Despliega Astro con el adaptador de Node en un VPS (yo uso Hetzner para esto — barato, rápido, confiable). Cada página se renderiza en la solicitud, cacheas agresivamente a nivel de Nginx o Cloudflare, y tienes actualizaciones de posts instantáneas. Esto es lo que haría para una operación de publicación real con más de 50,000 posts. Deploy Astro with the Node adapter to a VPS (I use Hetzner for this — cheap, fast, reliable). Every page renders on request, you cache aggressively at the Nginx or Cloudflare level, and you have instant post updates. This is what I'd do for a proper publishing operation over 50,000 posts.

¿Mi opinión honesta? La mayoría de los sitios no necesitan la complejidad de la Opción 1 o 3. Un rebuild de 4 minutos disparado por un webhook está bien para el 90% de los sitios de contenido.

---

Desempeño: Lo que realmente obtienes

En el proyecto de publicador del inicio — aquí es lo que pasó después de la migración a Astro:

  • LCP cayó de 7.2s a 1.1s en móvil (probado con WebPageTest desde un nodo en Londres)1.1s on mobile (tested with WebPageTest from a London node)
  • Total Blocking Time pasó de ~800ms a 0ms (cero JS por defecto, recuerda)0ms (zero JS by default, remember)
  • Su reporte de Core Web Vitals de Google Search Console fue de 3% de URLs "Buenas" a 91% "Buenas" dentro de seis semanas del despliegue91% "Good" within six weeks of deployment
  • Los costos de hosting bajaron porque su servidor WordPress ya no servía páginas, solo respuestas de API

Nada de eso es magia. Es solo lo que ocurre cuando quitas el renderizado de PHP de la ruta crítica y dejas de enviar un bundle de JavaScript de tema de 400kb a cada lector.

---

Los detalles que te van a complicar

Hablando claro — cosas que he tenido que depurar en proyectos reales:

  • Vistas previas de borradores. Esto es genuinamente molesto en una configuración headless. La vista previa nativa de WordPress depende de la representación del frontend. Necesitas construir un endpoint de vista previa personalizado en Astro que acepte un nonce de vista previa de WordPress y obtenga el borrador a través de WPGraphQL. No es difícil, pero toma un día hacerlo correctamente. This is genuinely annoying in a headless setup. WordPress's native preview relies on front-end rendering. You need to build a custom preview endpoint in Astro that accepts a WordPress preview nonce and fetches the draft via WPGraphQL. Not hard, but it takes a day to do properly.
  • Redirecciones. Si el sitio antiguo tenía cientos de redirecciones en .htaccess, ahora viven en el servidor de WordPress. Necesitas replicarlas en la configuración de Astro, o mantener WordPress accesible y hacer proxy de rutas específicas. He hecho ambas cosas. Replicar en Astro es más limpio a largo plazo. If the old site had hundreds of redirects in .htaccess, those live on the WordPress server now. You need to either replicate them in Astro's config, or keep WordPress accessible and proxy specific paths. I've done both. Replicating in Astro is cleaner long-term.
  • Búsqueda. La búsqueda integrada de WordPress es inútil en una configuración headless. Yo uso Algolia con el plugin WP Search with Algolia. Indexa tus posts en WP, consulta Algolia desde un componente Island de Astro. Funciona bien. WordPress's built-in search is useless in a headless setup. I use Algolia with the WP Search with Algolia plugin. Index your posts in WP, query Algolia from an Astro Island component. Works well.
  • Menús y navegación. Los menús de WordPress son extrañamente complicados de exponer a través de WPGraphQL. La ruta wpgraphql-acf a menudo termina siendo más limpia — simplemente modela tu nav como un repeater de ACF y listo. WordPress menus are weirdly fiddly to expose through WPGraphQL. The wpgraphql-acf route often ends up being cleaner — just model your nav as an ACF repeater and call it done.

---

FAQ

¿Necesito WPGraphQL o puedo usar el REST API?

Absolutamente puedes usar la REST API — viene integrada en el núcleo de WordPress y no requiere plugins adicionales. Para sitios simples con tipos de contenido estándar y campos personalizados mínimos, funciona perfectamente. Donde GraphQL se justifica es cuando tienes modelos de contenido complejos con muchos campos personalizados por tipo. Poder obtener exactamente los campos que necesitas en una sola solicitud, sin lidiar con parámetros _embed y llamadas REST anidadas, te ahorra tiempo en cada consulta que escribes. Tú decides. Yo simplemente encuentro GraphQL más limpio a partir de cierto nivel de complejidad._embed parameters and nested REST calls, saves time on every query you write. Up to you. I just find GraphQL cleaner past a certain complexity threshold.

¿Cómo manejo la autenticación de WordPress para contenido solo para miembros?

La autenticación JWT es el enfoque estándar. Instala el plugin JWT Authentication for WP REST API, emite tokens al iniciar sesión, pásalos en los encabezados de tu solicitud GraphQL. Del lado de Astro, lo manejarías con una ruta SSR (no estática) para que el contenido específico del usuario se obtenga del lado del servidor por cada solicitud. No intentes hacer esto de forma estática — ese camino lleva a la locura.JWT Authentication for WP REST API plugin, issue tokens on login, pass them in your GraphQL request headers. On the Astro side, you'd handle this with an SSR route (not static) so the user-specific content is fetched server-side per request. Don't try to do this statically — that way lies madness.

¿No es esto excesivo para un blog pequeño?

Sí, probablemente. Si tienes menos de 500 artículos y un editor, el costo de mantener una configuración headless no vale la pena. Solo usa un buen tema de WordPress, optimiza tus imágenes, y sigue adelante. Esta arquitectura se justifica cuando tienes volumen, complejidad editorial, o niveles de tráfico donde el desempeño realmente impacta los ingresos.

¿Cómo se ve la infraestructura de hosting en producción?

WordPress (solo CMS) en un VPS pequeño u host WordPress administrado — yo uso Kinsta o Cloudways. Frontend de Astro en Vercel, Netlify, o un VPS de Hetzner con Nginx dependiendo del proyecto. Cloudflare frente a todo. El costo mensual total para un sitio de contenido de tamaño medio es generalmente £60–£120, que a menudo es menos de lo que los clientes pagaban por un host WordPress todo en uno que estaba colapsando bajo la carga.less than what clients were paying for an all-in-one WordPress host that was struggling under the load.

---

El resumen honesto es este: WordPress headless con Astro es una de las mejores cosas que le han pasado a los sitios de contenido en un tiempo. No porque sea nuevo, sino porque las herramientas finalmente han alcanzado la idea. WPGraphQL es estable, el sistema de construcción de Astro es rápido, y las ganancias de desempeño son reales y medibles.

Consigue la arquitectura correcta desde el principio — especialmente tu estrategia de imágenes y tu enfoque de reconstrucción — y pasarás mucho menos tiempo apagando incendios después. Eso es realmente todo.

< BACK