wandres.dev
SERVER ISLANDS · server:defer y fallback

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.

⏱ 17 min

Una server island parece magia: una página estática y cacheable que, sin embargo, muestra tu nombre. La magia se deshace en cuanto ves el mecanismo, y el mecanismo es una pequeña obra de ingeniería criptográfica. La página no lleva la isla, lleva un hueco marcado, sus props encriptadas y un script diminuto que, tras cargar, pide el fragmento a un endpoint interno. El servidor descifra, renderiza y devuelve el HTML; el runtime lo cose en su sitio. Entender este baile —y por qué las props viajan cifradas— es entender qué es de verdad una server island: una llamada a función de servidor, diferida y sellada, cuyos argumentos cruzan el navegador.

🎯 Al terminar esta lección sabrás
  • Ver las dos fases: el marcador en el HTML y la petición que lo llena.
  • Entender por qué las props se serializan y se encriptan antes de viajar.
  • Conocer el endpoint interno bajo _server-islands y el contexto propio de la isla.
  • Saber qué es ASTRO_KEY y por qué es imprescindible 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.

<!-- 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.

⚠️
Encriptadas no es lo mismo que gratis

Las props se encriptan y se incrustan en el HTML, luego viajan de vuelta en la petición. Eso significa que unas props grandes engordan la página y el segundo viaje, y además pagan el cómputo de cifrar y descifrar. La lección de límites hará de esto una regla; por ahora, quédate con que cada prop paga peaje dos veces y un impuesto de cripto.

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.

// 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.

📝
El fragmento puede llegar en streaming

Como cualquier render de servidor en Astro, la isla puede emitir su HTML en streaming: el navegador pinta el principio del fragmento antes de que se genere el final. En islas con varias consultas, eso adelanta el primer contenido útil sin esperar a la más lenta.

ℹ️
Varias islas, varias peticiones en paralelo

Si una página tiene tres server islands, el navegador dispara tres peticiones independientes que se resuelven en paralelo. No hay un orden ni una dependencia entre ellas: cada una llena su hueco cuando termina, y si una falla o tarda, las demás no la esperan. Es el mismo aislamiento de las islas de cliente, ahora sobre peticiones de servidor en lugar de arranques de hidratación.

flowchart TD
R[render de la pagina] --> E[props serializadas y encriptadas]
E --> H[HTML con marcador fallback y script]
H --> L[tras cargar el script pide la isla]
L --> EP[endpoint interno server islands recibe las props]
EP --> D[el servidor descifra las props]
D --> RN[ejecuta el frontmatter con su propio contexto]
RN --> F[devuelve el fragmento HTML]
F --> S[el runtime cose el fragmento en el hueco]
style H fill:#f9e2af,color:#11111b
style EP fill:#89b4fa,color:#11111b
style D fill:#cba6f7,color:#11111b
style S fill:#a6e3a1,color:#11111b

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.

// 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.
ℹ️
Una clave, todo el despliegue

Piensa en ASTRO_KEY como el secreto compartido de un apretón de manos criptográfico que cruza el tiempo —del build a la petición— y el espacio —de una instancia a otra—. Sin una clave común, el cifrado del build y el descifrado del servidor hablan idiomas distintos, y las props horneadas se vuelven ilegibles. Es la diferencia entre “funciona en mi máquina con una sola instancia” y “funciona en producción con doce”.

Una server island es una llamada de servidor sellada que se despacha a través del cliente

Reduce el mecanismo a su esencia y verás un problema clásico con ropa nueva. Una server island es, en el fondo, una llamada a una función de servidor cuyos argumentos tienen que viajar por territorio hostil —el navegador, público e incontrolable— y volver intactos. El servidor de origen quiere invocar renderIsla(props) más tarde, pero no puede guardar props en su memoria porque quien dispara la llamada es el cliente, y el cliente no es de fiar. Así que sella los argumentos: los cifra para que no se lean y los autentica para que no se falsifiquen, y deja que sea el cliente quien los transporte de ida y de vuelta. Ese es exactamente el patrón de las cookies firmadas, de los JWT, de las capability URLs: cómo dejar que un cliente no confiable custodie un token que solo el servidor sabe acuñar y verificar. Una server island es una clausura diferida, sellada y despachada a través del cliente, y ASTRO_KEY es la clave con la que el servidor firma y abre esa clausura. Cuando lo ves así, la encriptación de props deja de parecer un detalle de implementación y se revela como el corazón conceptual del patrón: es lo que hace posible invocar el servidor desde una página que el servidor ya no controla. Todo lo demás —los marcadores que dan coordenadas, el endpoint interno que recibe la llamada, el swap que cose el resultado— es fontanería alrededor de esa idea. Y entenderla te vacuna contra los dos errores caros: meter secretos que no deberían viajar aunque vayan cifrados, y olvidar la clave estable que hace que la clausura siga abriéndose cuando tu sitio crece de una máquina a muchas.

⚔️ Diseca una server island en vivo
  1. Renderiza una página con una server island y abre el HTML: localiza los marcadores del hueco, el fallback y el bloque de props encriptadas incrustado.
  2. En la pestaña de red, encuentra la petición al endpoint bajo _server-islands y observa qué se envía y qué fragmento vuelve.
  3. Genera una ASTRO_KEY con astro create-key y razona por qué un despliegue con dos instancias fallaría sin ella.
  4. Comprueba que la isla lee cookies —personalízala según la sesión— y confirma que no puede acceder a una variable local de la página anfitriona.
  5. Pon dos islas en la misma página y verifica en la pestaña de red que sus peticiones salen en paralelo, sin esperarse.