SEO de despliegue para SvelteKit: adaptadores, prerenderizado y renderizado perimetral
El adaptador de SvelteKit y las opciones de prerenderizado por ruta deciden dónde y cuándo se renderizan las páginas, y eso determina el TTFB, el LCP y el presupuesto de rastreo. Un análisis a fondo centrado en el despliegue: cómo elegir entre adapter-static/node/vercel/cloudflare/netlify, prerender = true/false/'auto', las restricciones del entorno de ejecución perimetral y cómo crear sitemap.xml y robots.txt.
Idiomas
El adaptador de SvelteKit y la opción prerender por ruta deciden dónde y cuándo se renderiza una página (HTML estático durante la compilación, SSR en un servidor o SSR en el perímetro), y esa decisión determina el TTFB, que a su vez influye en el LCP y la capacidad de rastreo. adapter-static resulta adecuado para sitios puramente de contenido; un adaptador node/vercel/cloudflare con prerenderizado por ruta, para sitios mixtos de contenido y aplicación; y un adaptador perimetral, cuando importe el TTFB global, con las limitaciones de los arranques en frío y la ausencia de fs de Node. prerender = 'auto' es la herramienta para sitios mixtos. Los entornos de ejecución perimetrales no pueden leer el sistema de archivos. SvelteKit no genera sitemap.xml ni robots.txt: deben crearse como endpoints +server.js, con una estrategia dependiente del adaptador.
TL;DR — SvelteKit ya renderiza las páginas en el servidor: esa parte está resuelta. Esta página trata de la siguiente decisión: cómo se compila y despliega el sitio. Un adaptador empaqueta la aplicación de SvelteKit para un alojamiento (un host de archivos estáticos, un servidor Node o un servicio como Vercel o Cloudflare), y una opción prerender por página decide si esa página se convierte en un archivo HTML plano por adelantado o se renderiza de nuevo en cada visita. Esas dos decisiones determinan con qué rapidez obtienen el HTML los rastreadores. SvelteKit no crea el sitemap ni el robots.txt, por lo que deben añadirse expresamente.
Qué es un adaptador (en términos sencillos)
Un sitio creado con SvelteKit envía HTML real al navegador: el contenido está ahí antes de que se ejecute JavaScript. Ese es el problema difícil de SEO ya resuelto (si aún no lo está en una implementación concreta, el artículo de fundamentos de SvelteKit de esta misma sección cubre los modos de renderizado y la trampa del “contenedor vacío” que conviene evitar primero).
Un adaptador es el pequeño complemento que toma la compilación terminada de SvelteKit y la convierte en algo que un alojamiento concreto puede ejecutar. Evidence for this claim SvelteKit adapters transform a built application for deployment to a particular environment. Scope: SvelteKit adapters. Confidence: high · Verified: SvelteKit: Adapters El mismo sitio, un paquete distinto:
adapter-staticconvierte cada página en un archivo HTML plano, generado una sola vez. Excelente para un blog, documentación o un sitio de marketing que no cambia según el visitante.adapter-nodeenvuelve la aplicación en un servidor Node.js que debe operarse directamente.adapter-vercel,adapter-netlify,adapter-cloudflarela empaquetan para esos servicios de alojamiento, que renderizan las páginas bajo demanda, a veces en servidores perimetrales, físicamente cerca de los visitantes.
El contenido es idéntico en todos los casos. Lo que cambia es cuándo se genera el HTML (por adelantado o en cada petición) y dónde (un solo servidor o una red global).
Por qué esta es una decisión de SEO, no solo técnica
Lo principal: la velocidad. Una página que ya es un archivo estático se carga casi al instante. Una página que hay que construir en el servidor tarda un momento. Google ha señalado que, si un sitio “responds quickly for a while, the limit goes up, meaning more connections can be used to crawl. If the site slows down… the limit goes down and Google crawls less.” (traducción: «si el sitio responde rápido durante un tiempo, el límite aumenta; si se ralentiza, el límite disminuye y Google rastrea menos»). Un despliegue lento no solo molesta a los usuarios: puede hacer que Google lea menos contenido del sitio.
La versión sencilla de la decisión
- Un sitio de contenido (blog, documentación, marketing) →
adapter-staticcon prerenderizado completo. Es la opción más rápida y con menos puntos de fallo. - Un sitio de contenido con algunas partes dinámicas (búsqueda, comentarios) → un
adaptador de servidor (
node/vercel/cloudflare) con las páginas de contenido marcadas conprerender = true, dejando que las partes dinámicas se rendericen en cada petición. - Una aplicación o un panel con páginas personalizadas y con sesión iniciada → SSR para las rutas dinámicas y prerenderizado solo para las páginas públicas de marketing.
Los dos archivos que SvelteKit no crea
SvelteKit no genera automáticamente un sitemap.xml ni un robots.txt. Normalmente se añade
un pequeño endpoint (sitemap.xml/+server.js) y, para robots.txt, un archivo en la carpeta
static/ u otro endpoint. Evidence for this claim SvelteKit can serve static assets from its static directory and create custom responses with +server route files. Scope: Mechanisms for robots.txt and sitemap.xml; files are not generated automatically. Confidence: high · Verified: SvelteKit: Project structure SvelteKit: Routing Es fácil omitirlos porque muchos frameworks que renderizan HTML parecen completos. SvelteKit no incluye estos dos archivos.
La versión más profunda explica qué hace cada adaptador al renderizado, cómo
gestiona prerender = 'auto' un sitio mixto, por qué las funciones perimetrales no pueden leer archivos
y cómo cambia la estrategia de sitemap según el adaptador. Se encuentra en la pestaña
Avanzado.
TL;DR — El adaptador no cambia qué renderiza SvelteKit: cambia dónde y cuándo. Estático en tiempo de compilación (
adapter-static), en tiempo de petición en un servidor propio (adapter-node) o en tiempo de petición en funciones serverless/edge (adapter-vercel/-netlify/-cloudflare). Elprerender = truepor ruta genera HTML estático y elimina la ruta del manifiesto dinámico;prerender = 'auto'prerenderiza y la mantiene en el manifiesto: la herramienta para sitios mixtos con/blog/[slug]. Los runtimes edge se ejecutan sobre aislados de V8: sin elfsde Node, y los arranques en frío perjudican al TTFB, que alimenta el LCP y (según el documento de presupuesto de rastreo de Google) la capacidad de rastreo. SvelteKit no genera sitemap.xml ni robots.txt: hay que crearlos como endpoints+server.js, y conviene señalar que la estrategia depende del adaptador. Este es un complemento más acotado y centrado en el despliegue del artículo de fundamentos de SEO en SvelteKit de esta sección; se presupone que SvelteKit hace SSR por defecto y no se repite aquí esa explicación.
La idea que hace que todo esto encaje
El adaptador no cambia lo que se renderiza. Cambia dónde y cuándo.
Eso es todo. La documentación de SvelteKit lo expresa con precisión: los adaptadores “take the
built app as input and generate output for deployment.” (traducción: «toman la aplicación compilada como entrada y generan la salida para el despliegue»). Evidence for this claim SvelteKit adapters take the built application as input and generate deployment-specific output. Scope: Deployment output; adapter choice can still constrain supported runtime features. Confidence: high · Verified: SvelteKit: Adapters Los componentes, las
funciones load y los metadatos <svelte:head> son idénticos en todos los adaptadores.
Lo que difiere es:
- Cuándo se produce el HTML: en tiempo de compilación (estático/prerenderizado) o en tiempo de petición (SSR en un servidor, en una función serverless o en una función edge).
- Dónde se produce: en un único servidor de origen, en una función serverless regional o en una red edge cercana al visitante.
Todo lo que sigue es una consecuencia de esos dos ejes.
Por qué las decisiones de despliegue son decisiones de SEO
La cadena es corta y está bien documentada: TTFB → LCP → capacidad de rastreo.
El time to first byte es el tiempo que tarda el alojamiento en empezar a enviar la respuesta. Un archivo prerenderizado servido desde una caché de CDN tiene un TTFB casi nulo. Un servidor que tiene que renderizar la página tiene uno más alto. Una función serverless o edge que arranca en frío puede tener uno mucho más alto en el primer acceso. El TTFB es una entrada directa del Largest Contentful Paint (no se puede pintar lo que no se ha recibido) y el LCP es una señal de Core Web Vitals (Métricas web esenciales).
En el rastreo, Google es más explícito. Según la documentación sobre presupuesto de rastreo: “If the site responds quickly for a while, the limit goes up, meaning more connections can be used to crawl. If the site slows down or responds with server errors, the limit goes down and Google crawls less.” Y la línea de buenas prácticas: “Make your pages efficient to load. If Google can load and render your pages faster, we might be able to read more content from your site.” (traducción: «Si el sitio responde rápido, aumenta la capacidad de rastreo; si se ralentiza o devuelve errores, disminuye. Las páginas eficientes pueden permitir que Google lea más contenido»). Una función edge que arranca en frío y tarda en responder está sujeta a la misma dinámica que un servidor de origen lento.
Una nota de honestidad por delante: Google no publica ninguna guía específica para SvelteKit.
No hay ningún documento ni episodio de Search Off the Record que mencione los adaptadores de SvelteKit,
prerender = 'auto' o los arranques en frío en el perímetro. Aquí se aplica
la guía general de Google sobre renderizado y presupuesto de rastreo a los mecanismos específicos
de SvelteKit; no se atribuye a ningún representante una declaración específica sobre SvelteKit. El
planteamiento de Google de que “server-side or pre-rendering is still a great idea because
it makes your website faster for users and crawlers, and not all bots can run
JavaScript” (traducción: «el renderizado del servidor o el prerenderizado sigue siendo recomendable porque acelera el sitio para usuarios y rastreadores, y no todos los bots ejecutan JavaScript») es el anclaje oficial más cercano, y es agnóstico respecto al framework.
Cómo elegir un adaptador según los resultados de SEO
adapter-auto: el valor por defecto sin configuración, y su límite
Los proyectos nuevos de SvelteKit vienen con adapter-auto. Detecta la plataforma
(Vercel, Netlify, Cloudflare Pages, Azure, AWS) e instala el adaptador correspondiente
en tiempo de compilación. Es un buen punto de partida, pero tiene un límite claro que conviene
conocer: adapter-auto no acepta ninguna opción. Cuando se necesita
{ edge: true }, bindings de Cloudflare, ISR de Vercel o cualquier configuración específica
de la plataforma, debe instalarse directamente el adaptador subyacente (adapter-vercel,
adapter-cloudflare, etc.). auto debe tratarse como un andamiaje, no como una decisión de
producción.
adapter-static: SSG completo, para sitios centrados en el contenido
adapter-static prerenderiza todo el sitio como archivos estáticos durante la compilación. No se ejecuta ningún
servidor; un alojamiento sirve HTML plano. Evidence for this claim adapter-static prerenders a SvelteKit site as static files. Scope: Routes must be prerenderable; performance outcomes depend on hosting and page design. Confidence: high · Verified: SvelteKit: Static site generation Para un sitio centrado en el contenido este es el
perfil de SEO más sólido disponible: el TTFB más bajo, sin arranques en frío ni procesos que puedan
venir abajo. El único requisito es la trampa que se explica con detalle en el artículo de
fundamentos: el SSR debe permanecer activado durante la compilación; de lo contrario, se obtienen contenedores vacíos en lugar de
HTML renderizado. Aquí basta con señalar esa condición.
El inconveniente es la rigidez. Cualquier cosa que realmente necesite lógica de servidor en cada petición (una búsqueda real, contenido por usuario, gestión de formularios sin un endpoint de terceros) no puede vivir en una compilación puramente estática, que es exactamente para lo que sirven los siguientes adaptadores.
adapter-node: un servidor bajo control propio
adapter-node produce un servidor Node.js independiente que debe operarse y escalarse;
el TTFB queda bajo control propio. Es la opción más flexible y la que menos sorpresas de entorno de ejecución
depara: APIs completas de Node, incluido fs. Encaja bien cuando ya existe
infraestructura, se necesitan bibliotecas de Node que los entornos perimetrales no pueden ejecutar o se buscan
tiempos de respuesta predecibles (sin arranques en frío) desde un servidor caliente. La contrapartida es
operativa: debe mantenerse un servidor, y su velocidad y disponibilidad determinan la capacidad de
rastreo.
adapter-vercel: serverless, edge e ISR
adapter-vercel despliega en las funciones serverless de Vercel por defecto, con varias
palancas relevantes para el SEO que se configuran por ruta mediante export const config:
runtime: 'edge'traslada esa ruta al runtime edge de Vercel (más abajo se detalla).regionscontrola dónde se ejecutan las funciones serverless: la proximidad a los usuarios o a la base de datos reduce la latencia.isractiva Incremental Static Regeneration:isr: { expiration: 60 }sirve un recurso estático cacheado y lo regenera pasada esa ventana, ofreciendo “the performance and cost advantages of prerendered content with the flexibility of dynamically rendered content.” (traducción: «las ventajas de rendimiento y costo del contenido prerenderizado con la flexibilidad del contenido dinámico»). ISR es una cuarta vía genuina entre lo puramente estático y el SSR puro, pero atención a la advertencia de la propia documentación: “Using ISR on a route withexport const prerender = truewill have no effect, since the route is prerendered at build time.” (traducción: «ISR no tiene efecto en una ruta con prerender = true porque ya se prerenderiza durante la compilación»). ISR y prerender son alternativas, no acumulables.
adapter-cloudflare: Workers/Pages, edge global
adapter-cloudflare apunta a Cloudflare Workers y Pages: SSR en una red edge
global, a menudo el TTFB más bajo para una audiencia dispersa geográficamente. La restricción
importante es el runtime: los Workers se ejecutan sobre aislados de V8, no sobre Node. De la documentación:
“You can’t use fs in Cloudflare Workers.” (traducción: «No se puede usar fs en Cloudflare Workers»). Algunas APIs de Node solo funcionan tras el
flag de compatibilidad nodejs_compat, e incluso entonces el soporte no es uno a uno. Si
estaba leyendo archivos en tiempo de petición (un mapa de redirecciones, un archivo de datos, entradas para imágenes OG
personalizadas), ese código necesita replantearse; se trata en la sección sobre el edge más abajo.
(El antiguo adapter-cloudflare-workers está obsoleto; los proyectos nuevos usan
adapter-cloudflare, que cubre tanto Workers como Pages. Para instalaciones antiguas,
se recomienda migrar.)
adapter-netlify: funciones o Edge Functions (Deno)
adapter-netlify despliega en las funciones basadas en Node de Netlify por defecto, o en
Edge Functions basadas en Deno con edge: true. El mismo esquema que Vercel: serverless
por defecto con una opción de edge. Una nota específica de SvelteKit: los Netlify Forms
requieren que la página del formulario esté prerenderizada para que Netlify pueda detectar el marcado del formulario
en el momento del despliegue, un requisito de prerenderizado de esa ruta que se superpone
a la elección del adaptador.
La decisión, en una línea cada una
- Sitio puramente de contenido →
adapter-static, con prerenderizado completo. - Sitio de contenido con bolsas dinámicas →
adapter-node/-vercel/-cloudflare,prerender = trueen el contenido,false/'auto'en las rutas dinámicas. - Aplicación o panel con personalización → SSR en primer lugar (node o edge), con prerenderizado solo el armazón estático (marketing, inicio de sesión).
- Audiencia global con TTFB crítico → un adaptador edge para las rutas dinámicas, asumiendo las restricciones de las APIs de Node y la realidad de los arranques en frío.
(La pestaña Decision Tree recorre esto como un flujo ramificado.)
Estrategia de prerenderizado para sitios mixtos
Qué hacen realmente true / false / 'auto'
export const prerender es una opción de página por ruta (o por layout), y los tres
valores no son un simple encendido/apagado:
true: compila la ruta como HTML estático durante la compilación. Y algo crucial: queda “excluded from manifests used for dynamic SSR, making your server (or serverless/edge functions) smaller.” (traducción: «excluida de los manifiestos de SSR dinámico, lo que reduce el servidor o las funciones serverless/perimetrales»). Una vez prerenderizada, la ruta no puede recurrir al renderizado dinámico: es estática, y punto.false: renderiza siempre en cada petición. Sin archivo estático.'auto': la herramienta para sitios mixtos. Prerenderiza la ruta y la mantiene en el manifiesto dinámico del servidor, de modo que la misma ruta puede servirse de forma estática para las rutas conocidas y renderizarse en el servidor para el resto. Está pensada exactamente para el caso que describe la documentación: una ruta como/blog/[slug]“where you want to prerender your most recent/popular content but server-render the long tail.” (traducción: «prerenderizar el contenido reciente o popular y renderizar en servidor la cola larga»).
Como las rutas prerenderizadas reducen el bundle del servidor, un sitio mayoritariamente prerenderizado
con unas pocas rutas 'auto'/false despliega una función más pequeña, más barata y más rápida:
una ganancia de eficiencia independiente del SEO.
Las rutas dinámicas necesitan una función entries
El rastreador de prerenderizado descubre páginas siguiendo enlaces <a> desde sus puntos de
entrada. Eso funciona con rutas estáticas, pero una ruta dinámica como /blog/[slug] no tiene
ninguna URL fija que el rastreador pueda encontrar. Si nada enlaza a un slug determinado, SvelteKit
no sabrá que existe, y se topará con el clásico error de compilación de que las rutas “were
marked as prerenderable, but were not prerendered.”
La solución es una función entries explícita (o config.kit.prerender.entries) que
enumere los valores del parámetro:
// src/routes/blog/[slug]/+page.server.js
export const prerender = true;
export function entries() {
return [
{ slug: 'hello-world' },
{ slug: 'sveltekit-deployment-seo' },
];
}En la práctica, esa lista se genera a partir del CMS o del directorio de contenido. Sin ella, el prerenderizado solo cubre los slugs que el rastreador de enlaces encuentre por casualidad.
El patrón /blog/[slug] en la práctica
La configuración canónica de un sitio mixto combina prerender = 'auto' con una función entries que devuelve las entradas recientes y populares.
Esas obtienen HTML estático en tiempo de compilación; todo lo que no esté en la lista pasa al SSR
bajo demanda. Las entradas nuevas se renderizan dinámicamente hasta que la siguiente compilación las prerenderiza. Es
el término medio pragmático entre “prerenderizar las 40 000 entradas en cada compilación” y
“renderizar cada entrada en cada petición”.
Restricciones del runtime edge que afectan al SEO
config.runtime = 'edge' se aplica por ruta (en Vercel)
El edge no es un interruptor de todo o nada. En Vercel es una opción de página por ruta:
// +page.server.js or +server.js
export const config = { runtime: 'edge' };Esto permite llevar al perímetro las rutas almacenables en caché y de mucho tráfico para lograr un TTFB bajo, mientras las rutas dependientes de Node permanecen en el entorno serverless (Node) estándar dentro del mismo despliegue. La combinación debe ser deliberada.
Sin fs, sin APIs arbitrarias de Node
Los runtimes edge (Cloudflare Workers, Vercel Edge Functions, las Edge Functions basadas en Deno de
Netlify) no proporcionan el fs de Node. La documentación de Cloudflare: “You can’t use fs in
Cloudflare Workers.” La de Vercel: “You can’t use fs in edge functions.” (traducción: «No se puede usar fs en Cloudflare Workers ni en funciones perimetrales»). Ambas apuntan
a las mismas dos vías de escape: usar el helper read de $app/server para acceder a
los recursos incluidos en el bundle, o “prerender the routes in question” (traducción: «prerenderizar las rutas en cuestión») para que el acceso al archivo se produzca
en tiempo de compilación en lugar de en tiempo de petición.
Los casos relacionados con el SEO en los que esto duele: la generación dinámica de imágenes OG que lee una
fuente o un archivo de plantilla, los mapas de redirecciones basados en archivos o un endpoint de sitemap que lee
contenido del disco. Cualquiera de ellos pasa al read() de $app/server o pasa al
prerenderizado/tiempo de compilación. No es un bloqueante: es una restricción del tipo “conviene saberlo antes de elegir el edge”.
Arranques en frío y TTFB: cuándo ayuda el edge y cuándo no
Las funciones edge también arrancan en frío. Una función edge en frío, en su primera petición, puede ser más lenta que un servidor Node caliente, y muchísimo más lenta que un archivo prerenderizado servido desde caché. El edge gana cuando la función se mantiene caliente o cuando se combina con un almacenamiento en caché agresivo, de modo que la mayoría de las peticiones nunca llegan a la función. No es automáticamente la opción más rápida: “desplegar en el edge” no es sinónimo de “más rápido”. Para un sitio de contenido, la salida estática prerenderizada supera al SSR perimetral en TTFB siempre, porque no hay ninguna función que arrancar.
Generar sitemap.xml y robots.txt (SvelteKit no lo hará)
Esta es la laguna que la mayoría de los tutoriales de SvelteKit se saltan y que la mayoría de las auditorías detecta. SvelteKit no genera automáticamente ningún sitemap.xml ni ningún robots.txt, sea cual sea el adaptador y por muchas páginas que se prerendericen. Un sitio totalmente estático con miles de páginas prerenderizadas se publica igualmente sin sitemap salvo que se cree uno.
El patrón de endpoint +server.js
El sitemap idiomático es un endpoint de ruta que devuelve XML con el Content-Type
correcto:
// src/routes/sitemap.xml/+server.js
export const prerender = true; // needed on adapter-static
export async function GET() {
const urls = await getAllUrls(); // from your CMS/content
const body = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urls.map((u) => ` <url><loc>${u}</loc></url>`).join('\n')}
</urlset>`;
return new Response(body, {
headers: { 'Content-Type': 'application/xml' },
});
}La estrategia depende del adaptador
Aquí está la parte que une todo el artículo: la estrategia de sitemap es consecuencia de la elección de adaptador.
- En
adapter-static, el endpoint del sitemap necesitaexport const prerender = truepara incluirse en la salida estática: no hay ningún servidor en tiempo de ejecución que lo genere bajo petición. Se cocina en tiempo de compilación, lo que significa que solo está tan actualizado como la última compilación. - En un adaptador Node/serverless/edge, ese mismo endpoint puede generar el sitemap
dinámicamente en cada petición a partir del CMS o la base de datos: siempre actualizado, sin necesidad de recompilar.
(En un adaptador edge, debe recordarse la restricción de
fs: las URL se obtienen de una API o de un binding, no de una lectura de disco.)
Así que la pregunta de “¿mi sitemap debería ser estático o dinámico?” no es una decisión aparte: se desprende del adaptador elegido.
robots.txt: archivo estático frente a endpoint
Hay dos opciones. Un robots.txt plano puede colocarse en la carpeta static/ (se sirve en
/robots.txt automáticamente), que es la opción más sencilla y suficiente para la mayoría de los sitios.
También puede generarse desde un endpoint src/routes/robots.txt/+server.js cuando sea necesario que
cambie según el entorno (bloquear a los rastreadores en staging y permitirlos en
producción, por ejemplo). En cualquier caso, no deben bloquearse el bundle /_app/ ni el CSS:
eso rompe el renderizado para los motores que sí renderizan.
Si llega a esto desde el ángulo más amplio de los frameworks o del SEO en JavaScript, la lógica de “dónde y cuándo ocurre el renderizado” que se ve aquí es la misma que rige el SEO en JavaScript en general, y la pieza de fundamentos de SvelteKit de esta sección cubre los modos de renderizado y los patrones de metadatos sobre los que se construye este artículo.
Resumen con IA
Una versión condensada del contenido Advanced:
- El adaptador cambia dónde y cuándo, no qué. Los adaptadores “take the built app as input and generate output for deployment” (traducción: «toman la aplicación compilada como entrada y generan la salida para el despliegue»): el mismo contenido, distinto momento (tiempo de compilación frente a tiempo de petición) y distinta ubicación (origen frente a edge).
- Por qué es una decisión de SEO: TTFB → LCP → capacidad de rastreo. Google: si un sitio “responds quickly… the limit goes up… If the site slows down… Google crawls less.” (traducción: «si responde rápido, el límite aumenta; si se ralentiza, Google rastrea menos»). Ninguna guía de Google menciona SvelteKit específicamente: esta es una guía general aplicada a los mecanismos de SvelteKit.
- Adaptadores:
adapter-auto(sin configuración, sin opciones);adapter-static(SSG, sitios de contenido, el TTFB más bajo);adapter-node(servidor bajo control propio, APIs completas de Node);adapter-vercel(serverless + edge + ISR);adapter-cloudflare(Workers en el edge global, sinfs;adapter-cloudflare-workersestá obsoleto);adapter-netlify(funciones o Edge Functions de Deno). - Prerender:
truegenera HTML estático y elimina la ruta del manifiesto dinámico;falsesiempre hace SSR;'auto'prerenderiza y la mantiene dinámica: la herramienta para sitios mixtos con/blog/[slug](prerenderizar lo popular, SSR para la cola larga). - Las rutas dinámicas necesitan una función
entriespara evitar el error “marked as prerenderable, but were not prerendered” (traducción: «marcadas como prerenderizables, pero no prerenderizadas»). - Restricciones del edge:
runtime: 'edge'se aplica por ruta (Vercel); sinfs(“You can’t use fs in Cloudflare Workers” / funciones edge): debe usarse elread()de$app/servero prerenderizar; los arranques en frío pueden hacer que el edge sea más lento que un servidor caliente o un archivo estático. - Sin sitemap/robots.txt integrados. Debe crearse un endpoint
sitemap.xml/+server.js(prerender = trueenadapter-static; dinámico en adaptadores de servidor/edge). robots.txt mediantestatic/o un endpoint. - ISR ≠ prerender ampliado: “Using ISR on a route with
export const prerender = truewill have no effect.” (traducción: «ISR no tiene efecto en una ruta con prerender = true»). Son alternativas.
Documentación oficial
Documentación de fuente primaria de SvelteKit y de los motores de búsqueda.
SvelteKit
- Adapters • SvelteKit Docs — la visión general: los adaptadores toman la aplicación compilada y generan la salida de despliegue.
- Zero-config deployments (adapter-auto) • SvelteKit Docs — la detección por plataforma y la limitación de que “does not take any options” (traducción: «no acepta opciones»).
- Node servers (adapter-node) • SvelteKit Docs — el servidor Node independiente, las variables de entorno y el apagado ordenado.
- Static site generation (adapter-static) • SvelteKit Docs — SSG de todo el sitio, el requisito de SSR y la advertencia de SEO sobre el fallback de SPA.
- Vercel (adapter-vercel) • SvelteKit Docs —
runtime,regionsysplitpor ruta, e Incremental Static Regeneration. - Cloudflare (adapter-cloudflare) • SvelteKit Docs — Workers/Pages, los bindings de
platform.env,nodejs_compaty la limitación defs. - Cloudflare Workers (adapter-cloudflare-workers, deprecated) • SvelteKit Docs — el adaptador heredado obsoleto y la vía de migración.
- Netlify (adapter-netlify) • SvelteKit Docs — Node Functions frente a Edge Functions basadas en Deno (
edge: true), y el requisito de prerenderizado de Forms. - Page options (prerender, ssr, csr, config) • SvelteKit Docs —
prerender = true/false/'auto', la funciónentriesy elconfigpor ruta, incluidoruntime: 'edge'.
- Understand JavaScript SEO Basics — la cola de renderizado y el “not all bots can run JavaScript” (traducción: «no todos los bots pueden ejecutar JavaScript»).
- Optimize your crawl budget — la capacidad de rastreo ligada a la velocidad de respuesta; “make your pages efficient to load” (traducción: «hacer que las páginas carguen con eficiencia»).
Bing / Microsoft
- bingbot Series: JavaScript, Dynamic Rendering, and Cloaking. Oh My! — la recomendación de prerenderizado/renderizado dinámico de Bing y la aclaración sobre el cloaking.
- Fast Front-End Performance for Microsoft Bing — la propia arquitectura de SSR + CDN/nodos edge de Bing como prueba real.
Citas de la fuente
Declaraciones oficiales de la documentación de SvelteKit, de Google y de Bing. Cada enlace es un enlace profundo que salta al pasaje citado en la página de origen.
Documentación de SvelteKit: adaptadores y opciones de página
- “adapter-auto does not take any options.” — sobre el adaptador por defecto sin configuración. (Traducción: «adapter-auto no acepta opciones»). Ir a la cita
- Sobre
prerender = true: las rutas prerenderizadas quedan “excluded from manifests used for dynamic SSR, making your server (or serverless/edge functions) smaller.” (traducción: «excluidas de los manifiestos de SSR dinámico, lo que reduce el servidor o las funciones serverless/perimetrales»). Ir a la cita - Sobre
'auto': el caso de/blog/[slug]en el que se quiere “prerender your most recent/popular content but server-render the long tail.” (traducción: «prerenderizar el contenido reciente o popular y renderizar en servidor la cola larga»). Ir a la cita - Sobre la limitación de
fsen el edge: “You can’t use fs in Cloudflare Workers.” (traducción: «No se puede usar fs en Cloudflare Workers»). Ir a la cita - Sobre ISR frente a prerender en Vercel: “Using ISR on a route with export const prerender = true will have no effect, since the route is prerendered at build time.” (traducción: «ISR no tiene efecto en una ruta con prerender = true porque ya se prerenderiza durante la compilación»). Ir a la cita
Google: renderizado y presupuesto de rastreo
- “Keep in mind that server-side or pre-rendering is still a great idea because it makes your website faster for users and crawlers, and not all bots can run JavaScript.” (traducción: «El renderizado del servidor o el prerenderizado sigue siendo recomendable porque acelera el sitio para usuarios y rastreadores, y no todos los bots ejecutan JavaScript»). Ir a la cita
- “If the site responds quickly for a while, the limit goes up, meaning more connections can be used to crawl. If the site slows down or responds with server errors, the limit goes down and Google crawls less.” (traducción: «Si el sitio responde rápido, aumenta la capacidad de rastreo; si se ralentiza o devuelve errores, disminuye»). Ir a la cita
- “Make your pages efficient to load. If Google can load and render your pages faster, we might be able to read more content from your site.” (traducción: «Las páginas eficientes pueden permitir que Google cargue, renderice y lea más contenido»). Ir a la cita
Bing: prerenderizado y su propia arquitectura edge
- “We encourage detecting our bingbot user agent, prerendering the content on the server side and outputting static HTML for such sites…” — Fabrice Canel y Frédéric Dubut, Microsoft Bing. (Traducción: «Se recomienda detectar el agente de usuario bingbot, prerenderizar el contenido en el servidor y entregar HTML estático»). Ir a la cita
- “User traffic routes first to the closest CDN node (called an ‘edge node’).” — Bing Search Quality Insights, sobre la propia arquitectura de SSR + edge de Bing. (Traducción: «El tráfico de usuarios se dirige primero al nodo CDN más cercano, denominado nodo perimetral»). Ir a la cita
Lista de comprobación de SEO de despliegue para SvelteKit
Una revisión para confirmar que la configuración del adaptador, el prerenderizado y el sitemap no perjudica a los rastreadores:
- Se abandonó
adapter-autoen favor de un adaptador explícito cuando se necesita alguna configuración (edge, ISR, bindings). - El adaptador se corresponde con el tipo de sitio:
adapter-staticpara contenido puro, un adaptador de servidor/edge para cualquier cosa con lógica en cada petición. - Las rutas de contenido están en
prerender = true(o'auto'); solo las rutas genuinamente dinámicas se dejan al SSR. - Las rutas dinámicas mixtas (
/blog/[slug]) usanprerender = 'auto'con una funciónentriesque enumera las rutas conocidas. - No quedan errores de compilación sin resolver del tipo “marked as prerenderable, but were not prerendered”.
- Si alguna ruta usa
runtime: 'edge', no llama alfsde Node: el acceso a archivos usa elread()de$app/servero está prerenderizado. - Se tuvieron en cuenta los arranques en frío en edge/serverless: contenido cacheable, estático o caliente allí donde el TTFB importa.
- Existe un endpoint sitemap.xml (
prerender = trueenadapter-static; dinámico en adaptadores de servidor/edge). - Existe un robots.txt (en
static/o como endpoint+server.js) y no bloquea/_app/ni el CSS. - No se intenta acumular ISR sobre una ruta con
prerender = true(no tiene ningún efecto). - Se verificaron el HTML renderizado y la velocidad de respuesta en GSC URL Inspection y en PageSpeed Insights.
Los modelos mentales
1. Dónde y cuándo, no qué. El adaptador nunca cambia el contenido: cambia cuándo se genera el HTML (tiempo de compilación frente a tiempo de petición) y dónde (origen frente a edge). Toda pregunta de SEO sobre el despliegue se reduce a esos dos ejes, que deben definirse antes de modificar la configuración.
2. Prerender elimina una ruta del servidor.
prerender = true no solo vuelve estática la ruta: la saca del manifiesto
dinámico. Eso reduce la función y descarta una alternativa dinámica.
'auto' es la excepción: prerenderizada y aún en el manifiesto.
3. La división de /blog/[slug].
El patrón predeterminado para sitios de contenido reales prerenderiza las entradas conocidas
(la función entries devuelve las recientes o populares) y deja la cola larga a SSR. 'auto' es el
interruptor que hace que ambas cosas sean ciertas a la vez.
4. El edge es un intercambio, no una mejora.
El perímetro aporta proximidad geográfica (TTFB bajo cuando está caliente) a cambio de APIs de Node
(sin fs) y riesgo de arranques en frío. Solo a veces supera a un servidor Node caliente, y
siempre pierde en TTFB frente a la salida estática prerenderizada. Debe elegirse por un motivo, no por
defecto.
5. El sitemap va detrás del adaptador.
“¿Sitemap estático o dinámico?” no es una decisión aparte. adapter-static →
sitemap prerenderizado, actualizado solo en cada compilación. Adaptador de servidor/edge → sitemap por
petición, siempre actualizado. El adaptador ya respondió a la pregunta.
6. Nada genera esos dos archivos. SvelteKit no crea ningún sitemap.xml ni ningún robots.txt, con ningún adaptador. Si no se crearon expresamente, no existen. Este requisito debe incorporarse a la lista de lanzamiento.
Cómo elegir la combinación de adaptador y prerenderizado
La pregunta central del despliegue de SvelteKit es ¿cómo debe publicarse este sitio? El flujo es el siguiente:
1. ¿Alguna página necesita lógica de servidor en cada petición: autenticación, personalización, búsqueda en vivo, gestión de formularios, datos por usuario? → No (todas las páginas son iguales para todos los visitantes): continuar en 2. → Sí: continuar en 3.
2. Sitio puramente de contenido (blog, documentación, marketing).
→ adapter-static con prerender = true en todo el sitio (o en el layout
raíz), un sitemap.xml/+server.js prerenderizado (prerender = true) y un
static/robots.txt. El TTFB más bajo, sin arranques en frío, nada que ejecutar. Pare aquí.
3. ¿Es dinámico todo el sitio o solo algunas rutas? → Solo algunas rutas (sobre todo contenido, con algunas partes dinámicas): continuar en 4. → Mayoritaria o totalmente dinámico (aplicación, panel, ecommerce con datos por usuario): continuar en 5.
4. Sitio de contenido con bolsas dinámicas.
→ Un adaptador de servidor/edge (adapter-node, -vercel o
-cloudflare), con las rutas de contenido en prerender = true y las dinámicas en
false. Para rutas del estilo /blog/[slug] con contenido de popularidad conocida, se usa
prerender = 'auto' más una función entries. El sitemap se genera dinámicamente
desde el CMS.
5. Aplicación / panel / ecommerce (SSR primero). A continuación se define dónde se ejecuta el SSR:
→ Latencia predecible, bibliotecas de Node e infraestructura existente: adapter-node
(servidor caliente, APIs completas de Node, sin sorpresas de arranque en frío).
→ Audiencia global, el TTFB es lo que más importa, sin dependencias pesadas de Node: un adaptador edge
(adapter-cloudflare, o adapter-vercel con runtime: 'edge' por ruta),
con la condición de que no hay fs (debe usarse el read() de $app/server o prerenderizar) y existen arranques en frío.
Solo se prerenderiza el armazón realmente estático (marketing, inicio de sesión).
Cuarta vía (solo en Vercel): si una ruta es “casi estática pero cambia de vez en
cuando”, puede considerarse ISR (isr: { expiration }) en lugar de prerender = true,
nunca ambos, ya que “ISR on a route with export const prerender = true will have
no effect.”
¿Mi sitemap debería ser estático o dinámico?
¿Está en adapter-static? → Endpoint de sitemap estático/prerenderizado
(prerender = true). Se cocina en la compilación; está bien para sitios que se recompilan al publicar.
¿Está en un adaptador Node/serverless/edge? → Sitemap dinámico generado en cada petición
desde el CMS o la base de datos: siempre actualizado, sin recompilar. (En el edge, las URL se obtienen de una API o
de un binding, no de una lectura de disco con fs.)
Nunca: publicar sin sitemap porque “todas las páginas son estáticas”. La salida estática y la descubribilidad mediante sitemap no están relacionadas: SvelteKit no genera ninguno de los dos archivos con ningún adaptador.
SEO de despliegue para SvelteKit: hoja de referencia
Los adaptadores de un vistazo
| Adaptador | Renderiza | Runtime | Nota de SEO |
|---|---|---|---|
adapter-static | En tiempo de compilación (SSG) | ninguno | El TTFB más bajo, sin arranques en frío; sitios de contenido |
adapter-node | En tiempo de petición (SSR) | Node | APIs completas de Node (fs ✅); servidor operado directamente |
adapter-vercel | En tiempo de petición | serverless / edge | runtime y regions por ruta, ISR |
adapter-cloudflare | En tiempo de petición | edge V8 | Edge global; sin fs; nodejs_compat |
adapter-netlify | En tiempo de petición | Node / edge Deno | edge: true para Deno Edge Functions |
adapter-auto | (detecta los anteriores) | — | No acepta opciones: solo andamiaje |
Valores de prerender
| Valor | ¿HTML estático? | ¿En el manifiesto dinámico? | Para qué usarlo |
|---|---|---|---|
true | ✅ | ❌ (eliminada) | Rutas de contenido estático conocidas |
false | ❌ | ✅ | Rutas genuinamente dinámicas |
'auto' | ✅ | ✅ | /blog/[slug]: prerenderizar lo popular, SSR para la cola larga |
Reglas rápidas
- Rutas dinámicas prerenderizadas → requieren una función
entriespara evitar el error de “not prerendered”. runtime: 'edge'se aplica por ruta (Vercel): pueden combinarse rutas edge y Node.- Edge = sin
fs→ debe usarse elread()de$app/servero prerenderizar. - Los arranques en frío hacen que el edge sea más lento que un servidor caliente o un archivo estático en el primer acceso.
- ISR ≠ prerender:
isren una ruta conprerender = trueno hace nada. - Sin sitemap/robots.txt automáticos: ambos deben crearse.
prerender = trueen el endpoint del sitemap paraadapter-static; dinámico en servidor/edge. - Nunca deben bloquearse
/_app/ni el CSS en robots.txt.
Falta una ruta prerenderizada en el despliegue
Causa probable: el rastreador no pudo descubrir la ruta, falta un valor de entries o el prerenderizado falló. Solución: añadir enlaces rastreables o entradas explícitas y tratar las advertencias de compilación como fallos de publicación. Confirmación: el manifiesto de salida contiene la ruta y producción devuelve HTML completo.
adapter-static falla en una ruta dinámica
Causa probable: la ruta no se puede enumerar por completo en tiempo de compilación. Solución: proporcionar entradas finitas, rediseñar la ruta o usar un adaptador con capacidad de servidor para esa ruta. Confirmación: el adaptador elegido compila y cada ruta representativa devuelve la respuesta prevista.
El despliegue en el edge lanza errores del sistema de archivos o de APIs de Node
Causa probable: el código de la ruta o una dependencia asume funciones de Node que no están disponibles en el runtime edge. Solución: sustituir la dependencia, trasladar el trabajo a un servicio compatible o elegir un adaptador Node. Confirmación: el SSR en producción se ejecuta sin excepciones en tiempo de ejecución.
El sitemap o el robots.txt devuelve HTML
Causa probable: una ruta alternativa captura el endpoint o el controlador +server establece un cuerpo o unas cabeceras incorrectos. Solución: crear controladores de endpoint explícitos con los tipos de contenido correctos. Confirmación: las peticiones directas devuelven la respuesta de texto/XML esperada y el estado 200.
Los metadatos difieren entre las rutas prerenderizadas y las de SSR
Causa probable: los datos de la cabecera se cargan en rutas de código distintas o dependen del estado del navegador. Solución: centralizar la generación de metadatos a partir de datos de página seguros para el servidor o la compilación. Confirmación: el HTML sin procesar de ambos tipos de ruta contiene una lógica equivalente de título, URL canónica y robots.
Confirmar qué publicó realmente el adaptador
El sentido de elegir un adaptador y prerenderizar es que un rastreador obtenga HTML rápido y completo. Estas comprobaciones confirman que eso es lo que ha ocurrido realmente, desde la respuesta en bruto, no desde el navegador.
¿La página está prerenderizada o renderizada con SSR (contenido en el HTML en bruto)?
Un curl sencillo no ejecuta JavaScript, así que ve exactamente lo que ve un rastreador que no
renderiza.
macOS / Linux
# Raw HTML as the host sends it (no JS executed)
curl -sL "https://example.com/your-page/" -o raw.html
grep -o "Your unique headline text" raw.html # empty = CSR shell, not prerenderedWindows (PowerShell)
Invoke-WebRequest -Uri "https://example.com/your-page/" -OutFile raw.html
Select-String -Path raw.html -Pattern "Your unique headline text"¿El TTFB es rápido (o hay una función arrancando en frío)?
El TTFB influye en el LCP y la capacidad de rastreo, por lo que debe medirse con accesos a la URL en frío y luego en caliente:
# Time to first byte, twice — a big first number then a small one = cold start
for i in 1 2; do
curl -s -o /dev/null -w "TTFB: %{time_starttransfer}s\n" "https://example.com/your-page/"
doneUna página prerenderizada/estática debería ser sistemáticamente baja. Un primer valor alto que baja en el segundo acceso es un arranque en frío clásico de serverless/edge.
¿Esta ruta se prerenderizó o se sirvió dinámicamente?
Los alojamientos estáticos y las CDN suelen revelarlo en las cabeceras (estado de caché, age,
x-vercel-cache, cf-cache-status):
curl -sI "https://example.com/your-page/" | grep -iE "cache|age|x-vercel|cf-"Un HIT (o un age distinto de cero) significa que se le está sirviendo contenido cacheado/prerenderizado;
un MISS/DYNAMIC en cada petición significa que se está renderizando en cada petición.
¿El sitemap existe realmente y devuelve XML?
Dado que SvelteKit no genera ninguno, debe verificarse que el sitemap está disponible con el tipo de contenido correcto:
curl -sI "https://example.com/sitemap.xml" | grep -iE "HTTP/|content-type"
# Want: 200 + content-type: application/xml (not text/html or a 404)Una línea para la consola de DevTools
La línea puede pegarse en la consola del navegador para comparar el DOM renderizado con lo que necesita un rastreador:
si el titular aparece allí pero falta en la salida de curl anterior, se está renderizando en el
cliente:
// Is the content in the DOM, and does the sitemap resolve?
console.log('headline in DOM:', document.body.innerText.includes('Your unique headline text'));
fetch('/sitemap.xml').then(r => console.log('sitemap status:', r.status, r.headers.get('content-type')));Comprobar que robots.txt no bloquea el bundle
curl -sL "https://example.com/robots.txt" | grep -iE "disallow.*(/_app|\.js|\.css)"Un Disallow que coincida con /_app/ (la salida empaquetada de SvelteKit) o con el CSS significa que los
motores no pueden renderizar la página: casi siempre es un error.
Herramientas para depurar el SEO de despliegue en SvelteKit
- Inspección de URL (Google Search Console): la fuente de referencia. Se ejecuta una prueba en vivo de una URL y se revisan el HTML renderizado, la captura de pantalla y los recursos de la página para confirmar que el contenido y los metadatos están presentes y que no hay nada bloqueado.
- PageSpeed Insights: la herramienta que recomienda la propia documentación de SvelteKit; muestra el TTFB y las Core Web Vitals (Métricas web esenciales) (LCP/INP/CLS) a las que más afecta la elección del adaptador.
- WebPageTest: cascada y tira de fotogramas para diagnosticar el TTFB y los tiempos de arranque en frío en despliegues edge/serverless.
curl -w "%{time_starttransfer}": la comprobación más rápida del TTFB en bruto y del arranque en frío (véase la pestaña Scripts).- Paneles del alojamiento (Vercel / Cloudflare / Netlify Analytics): número de invocaciones de funciones, tasas de arranque en frío y tasas de acierto de caché por ruta; la verdad sobre el terreno para saber si edge/serverless es realmente rápido en una implementación concreta.
- Screaming Frog SEO Spider: rastree con el renderizado de JS activado y desactivado para comparar el HTML en bruto con el renderizado en todo el sitio y confirmar que las rutas prerenderizadas están completas.
- Ahrefs Site Audit: detecta sitemaps ausentes o bloqueados, cadenas de redirecciones, canonicals rotos y problemas de indexabilidad a escala.
Evaluación: SEO de despliegue para SvelteKit
Cinco preguntas rápidas sobre adaptadores, prerenderizado y renderizado perimetral en SvelteKit. Se elige una respuesta para cada una y después se comprueba el resultado.
Recursos recomendados
Mis textos relacionados
- JavaScript SEO: A Definitive Guide — mi referencia completa sobre los modos de renderizado (SSR, renderizado estático, prerenderizado y las trampas del CSR) que sustentan cada decisión de adaptador de aquí. Como digo allí, cualquier configuración de SSR, renderizado estático o prerenderizado va a funcionar bien para los motores de búsqueda, que es exactamente la red de seguridad que hay detrás de estas elecciones de adaptador.
- The Beginner’s Guide to Technical SEO — dónde encajan el renderizado, el rastreo y las Core Web Vitals (Métricas web esenciales) en el panorama general.
Mis ponencias
- How Search Works (SlideShare) — mi recorrido por el rastreo, el renderizado, la indexación y el ranking, que es el trasfondo de por qué importan el TTFB y el momento del renderizado. (Se aplica mi advertencia habitual: “This is my understanding of systems… not going to be 100% complete or accurate.”)
Del sector
- Adapters • SvelteKit Docs — la visión general autorizada de todos los adaptadores oficiales y de cómo se especifican en
svelte.config.js. - Page options (prerender, ssr, csr, config) • SvelteKit Docs — los valores de
prerenderpor ruta, la funciónentriesy elconfigpor ruta, incluidoruntime: 'edge', en palabras del propio equipo. - Vercel (adapter-vercel) • SvelteKit Docs — el runtime edge, las regiones y la advertencia de ISR frente a prerender.
- Cloudflare (adapter-cloudflare) • SvelteKit Docs — el despliegue en Workers/Pages, los bindings y la limitación de
fs. - SvelteKit • Cloudflare Pages docs — los mecanismos de despliegue y los bindings de
platformdesde el lado de Cloudflare. - SvelteKit SEO: Your Secret Weapon (Okupter) — una guía práctica sobre prerenderizado, metaetiquetas y el patrón de sitemap/RSS con
+server.js. - A Deep Dive into SvelteKit’s Rendering Techniques (This Dot Labs) — los mecanismos de SSR/SSG/CSR y la configuración por ruta y por layout, con la contrapartida de carga en el servidor de que “SSR can be expensive”.
- Understand JavaScript SEO Basics (Google Search Central) — la cola de renderizado y el “not all bots can run JavaScript”, la guía general bajo la que se sitúa cada elección de adaptador.
Registro de cambios
Actualizado el 14 ago 2026.
Resumen editorial y detalles registrados del cambio.Detalles del cambio
-
Los detalles del cambio están disponibles actualmente en inglés.
La comparación completa no está disponible: no se archivó una instantánea anterior para esta revisión.
Actualizado el 2 ago 2026.
Resumen editorial y detalles registrados del cambio.Detalles del cambio
-
Los detalles del cambio están disponibles actualmente en inglés.
La comparación completa no está disponible: no se archivó una instantánea anterior para esta revisión.