Saltar al contenido

Cómo funcionan las server islands por dentro

El mecanismo real: la página deja un marcador y un script en lugar de la isla, las props se serializan y se encriptan para viajar por territorio hostil, y tras cargar la página el navegador hace una petición separada a un endpoint interno bajo _server-islands. El servidor descifra las props, renderiza el fragmento y el runtime lo inserta. Y por qué ASTRO_KEY es la pieza que lo sostiene en producción.

Dos fases: el marcador y la petición que lo llena

Cuando Astro compila una página con server:defer, no renderiza la isla en línea. En su lugar deja tres cosas en el HTML: unos marcadores —comentarios que delimitan el hueco—, el contenido del fallback y un script de runtime que sabe cómo pedir la isla.

La página, así, se puede prerenderizar y cachear entera, porque nada de lo que contiene depende todavía del visitante.

ASTRO
<!-- Lo que la pagina estatica lleva, esquematizado --><!--[astro-island: marcador de apertura con props cifradas]--><span class="carrito-esqueleto">Carrito</span><script>/* runtime que pedira la isla tras cargar */</script><!--[astro-island: marcador de cierre]-->

Esos comentarios no son decorativos: son las coordenadas que el runtime usa para saber dónde empieza y acaba el hueco. Tras cargar la página, el script entra en acción y dispara la segunda fase: una petición HTTP separada, hecha desde el navegador, que trae el HTML real de la isla.

El runtime localiza el hueco por sus marcadores y sustituye el fallback por lo que devuelve el servidor. Dos fases, dos momentos, dos peticiones: la de la página —cacheable— y la de la isla —viva—.

El ciclo completo, de un vistazo:

  • Build o render: se cifran las props y se incrustan con el fallback y el script.
  • Primer paint: el navegador muestra el fallback; la página ya es utilizable.
  • Segunda petición: el script pide la isla al endpoint interno con las props cifradas.
  • Render y swap: el servidor descifra, renderiza y el fragmento sustituye al fallback.
  • Aislamiento: varias islas repiten el ciclo en paralelo, sin esperarse.

La encriptación de las props y por qué

Aquí está la parte sutil. Una isla se renderiza en el servidor, pero se invoca desde el navegador, y necesita sus props para renderizarse.

¿Cómo llegan esas props del servidor de origen al render de la isla, si en medio está el cliente? La respuesta de Astro: las props se serializan, se encriptan y se incrustan en el HTML; el script las envía de vuelta al servidor en la petición de la isla.

Conviene una aclaración: el fallback no se cifra —es HTML público desde el inicio—; solo las props viajan selladas. Proteges los argumentos de la isla, no su marcador ni su hueco.

Que viajen cifradas no es un lujo, es una necesidad de dos caras. La primera es confidencialidad: el HTML es público y cacheable, así que cualquier dato que pases como prop —un identificador interno, un flag— quedaría a la vista de todos si viajara en claro.

La segunda, más profunda, es integridad: si las props viajaran sin sellar, un cliente malicioso podría falsificarlas y pedirle al servidor que renderice la isla con los argumentos que se le antojaran. La encriptación autenticada las convierte en un token que solo el servidor puede acuñar y verificar: ni se leen, ni se falsifican.

Repara en un matiz temporal que lo complica todo: cuando la página se prerenderiza en el build, las props se cifran entonces, y quedan horneadas en el HTML estático. El cifrado no ocurre en cada petición, sino una vez, y su resultado se cachea con la página.

Esto significa que la clave que cifró en el build tiene que estar disponible, idéntica, en el servidor que descifre más tarde. Guarda esta idea: es la raíz de por qué ASTRO_KEY existe.

El endpoint interno y el contexto de la isla

La petición de la segunda fase no va a una ruta que tú escribas: va a un endpoint interno que Astro reserva bajo el prefijo _server-islands.

El runtime le envía las props encriptadas; el servidor las descifra, ejecuta el frontmatter del componente de la isla —de nuevo, en el servidor, en este instante— y devuelve el HTML resultante, que puede llegar en streaming.

TypeScript
// Esquema conceptual de lo que ocurre en el servidor// 1. entra la peticion a  /_server-islands/CarritoResumen// 2. descifra las props con la clave// 3. ejecuta el frontmatter del componente con esas props// 4. devuelve el fragmento HTML, posiblemente en streaming

Que el frontmatter se ejecute de nuevo aquí tiene una consecuencia importante: la isla es una unidad de render completa e independiente, con su propio ciclo de vida. Consulta sus datos, hace sus await, arma su HTML, todo dentro de esa petición aislada. Y un detalle decisivo: la petición de la isla lleva las cookies del navegador.

Por eso una server island puede leer la sesión y personalizar de verdad —saber quién eres— a través de su propio Astro.request. La isla tiene su propio contexto de petición, distinto del de la página que la alojó.

Con ese aislamiento viene una limitación que conviene grabar: la isla no ve las variables locales de la página que la contiene; solo recibe las props que le pasaste, ya descifradas. Se renderiza sola, como una función pura de sus argumentos.

Si necesita un dato, o se lo pasas como prop —pagando el peaje del cifrado— o lo consulta ella misma dentro de su render.

La clave de encriptación en producción

Si las props se cifran, hace falta una clave. En desarrollo Astro genera una automáticamente y no tienes que pensar en ella.

En producción, no pensar en ella te romperá el sitio, y por la razón temporal que anticipamos: cuando una página se prerenderiza en el build, sus props se cifran con la clave de ese build y quedan horneadas en el HTML. Más tarde, en cada petición de la isla, un servidor tiene que descifrarlas.

Si ese servidor no comparte la misma clave que hizo el cifrado, no podrá abrirlas.

Esto salta a la vista en dos escenarios normalísimos: un despliegue con varias instancias detrás de un balanceador —cada una necesita la misma clave para descifrar props que quizá cifró otra— y reconstrucciones sucesivas, donde el HTML viejo cacheado debe seguir siendo descifrable por el servidor nuevo.

La solución es fijar una clave estable con la variable de entorno ASTRO_KEY, que generas una vez y compartes en todo el despliegue.

TypeScript
// Genera una clave estable una sola vez://   astro create-key//// Publicala como variable de entorno en produccion://   ASTRO_KEY=<la clave generada>//// Todas las instancias y todos los builds deben compartirla.