Guide SvelteKit Deployment SEO: Adapters, Prerendering, and Edge Rendering

SvelteKit's adapter and per-route prerender settings decide où and quand votre pages render — and que drives TTFB, LCP, and budget d’exploration. A deployment-focused deep dive: choosing adapter-static/node/vercel/cloudflare/netlify, prerender = vrai/faux/'auto', edge-runtime constraints, and building sitemap.xml and robots.txt.

Première publication : 3 juil. 2026 · Dernière mise à jour : 3 août 2026 · Advanced
Langues

SvelteKit's adapter and per-route prerender setting decide où and quand une page renders — static HTML at construire temps, SSR on a server, or SSR at the edge — and que decision drives TTFB, qui feeds LCP and explorer capacity. Pick adapter-static pour pure content sites, a node/vercel/cloudflare adapter with per-route prerender pour mixed content-plus-app sites, and an edge adapter quand global TTFB matters (accepting cold starts and aucun Node fs). prerender = 'auto' is the mixed-site outil. Edge runtimes can't lire the filesystem. And SvelteKit generates aucun sitemap.xml or robots.txt — vous construire ceux as +server.js endpoints, with the strategy selon votre adapter.

TL;DR — The adapter doesn’t modifier ce que SvelteKit renders — it changements and quand: build-time static (adapter-static), request-time on a server vous run (adapter-node), or request-time on serverless/edge functions (adapter-vercel/-netlify/-cloudflare). Per-route prerender = true builds static HTML and drops the route from the dynamic manifest; prerender = 'auto' prerenders and garde it in the manifest — the outil pour mixed /blog/[slug] sites. Edge runtimes run on V8 isolates: aucun Node fs, and cold starts hurt TTFB, qui feeds LCP and (per Google’s crawl-budget doc) explorer capacity. SvelteKit generates aucun sitemap.xml or robots.txt — construire les as +server.js endpoints, and remarque the strategy dépend on the adapter. Ce is a narrower, deployment-focused companion to the SvelteKit SEO fundamentals article in ce section; I assume vous déjà know SvelteKit is SSR-by-default and won’t re-litigate que ici.

The un idea que rend tout of ce click

The adapter fait pas modifier ce que renders. It changements and quand. That’s the whole chose. The SvelteKit docs put it precisely: adapters “prendre the construit app as input and generate output pour deployment.” 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 Votre components, votre load functions, votre <svelte:head> metadata — identical à travers every adapter. Ce que differs is:

  • Quand the HTML is produced: at construire temps (static/prerendered) or at requête temps (SSR on a server, serverless function, or edge function).
  • it’s produced: on a unique origin server, on a regional serverless function, or on an edge network fermer to the visitor.

Everything ci-dessous is a consequence of ceux two axes.

Pourquoi deployment choices are SEO choices

The chain is short and well-documented: TTFB → LCP → explorer capacity.

Temps to premier byte is how long the host takes to commencer sending la réponse. A prerendered fichier served from a CDN cache has a near-zero TTFB. A server que has to render lune page has a plus élevé un. A cold-starting serverless or edge function peut have a beaucoup plus élevé un on the premier hit. TTFB is a direct input to Largest Contentful Paint — vous pouvez’t paint ce que vous haven’t reçu — and LCP is a Core Web Vitals signal.

The explorer side is où Google is la plupart explicit. From the crawl-budget documentation: “Si le site responds quickly pour a pendant que, the limite goes up, meaning plus connections peut be utilisé to explorer. Si le site slows bas or responds with server errors, the limite goes bas and Google crawls moins.” And the best-practice line: “Faire votre pages efficient to charger. Si Google peut charger and render votre pages faster, we pourrait be able to En savoir plus content from votre site.” A cold-starting edge function that’s slow to respond is subject to the même dynamic as a slow origin server.

Un honesty remarque up front: Google publishes aucun SvelteKit-specific guidance. There’s aucun doc or Search Off the Record episode naming SvelteKit adapters, prerender = 'auto', or edge cold starts. Ce que I’m doing ici is applying Google’s general rendering and crawl-budget guidance to SvelteKit’s spécifique mechanics — pas quoting a rep who commented on SvelteKit, parce que none has. The Google framing que “server-side or pre-rendering is encore a great idea parce que it rend votre website faster pour utilisateurs and robots d’exploration, and pas tout bots peut run JavaScript” is the closest official anchor, and it’s framework-agnostic.

Choosing an adapter pour le SEO outcomes

adapter-auto — the zero-config par défaut, and its ceiling

Nouveau SvelteKit projects ship with adapter-auto. It detects the platform — Vercel, Netlify, Cloudflare Pages, Azure, AWS — and installs the matching adapter at construire temps. It’s a fine starting point, but there’s a hard ceiling worth knowing: adapter-auto ne fait pas prendre quelconque options. The moment vous besoin { edge: true }, Cloudflare bindings, Vercel ISR, or quelconque platform-specific configuration, vous install the underlying adapter (adapter-vercel, adapter-cloudflare, etc.) directement. Treat auto as a scaffold, pas a production decision.

adapter-static — complet SSG, pour content-first sites

adapter-static prerenders votre whole site to static fichiers at construire temps. Aucun server runs; a host sert flat HTML. 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 Pour a content-first site ce is the strongest SEO profile vous pouvez have — lowest TTFB, aucun cold starts, nothing to fall over. The un requirement is the trap covered at length in the fundamentals article: SSR doit stay on during the construire, or vous obtenir vide shells au lieu de rendered HTML. I won’t re-explain que ici au-delà flagging it.

The catch is rigidity. Anything que genuinely nécessite server logic per requête (vrai search, per-user content, formulaire handling sans a third-party endpoint) can’t live on a purely static construire — qui is exactly ce que the suivant adapters are pour.

adapter-node — a server vous contrôler

adapter-node produces a standalone Node.js server. Vous run it, vous scale it, vous propre the TTFB. Ce is the la plupart flexible option and the un with the fewest runtime surprises — complet Node APIs, notamment fs. It’s a bon fit quand vous have infrastructure déjà, besoin Node libraries que edge runtimes can’t run, or vouloir predictable (non-cold-starting) réponse times from a warm server. The tradeoff is operational: you’re running a server, and its speed and uptime are now votre explorer capacity.

adapter-vercel — serverless, edge, and ISR

adapter-vercel deploys to Vercel’s serverless functions by par défaut, with several SEO-relevant levers définir per route via export const config:

  • runtime: 'edge' moves que route to Vercel’s edge runtime (plus ci-dessous).
  • regions contrôle où serverless functions run — closer to votre utilisateurs (or votre database) signifie lower latency.
  • isr enables Incremental Static Regeneration: isr: { expiration: 60 } sert a mis en cache static asset and regenerates it après the window, giving “the performances and cost advantages of prerendered content with the flexibility of dynamically rendered content.” ISR is a genuine fourth chemin entre pure-static and pure-SSR — but remarque the docs’ propre caveat: “En utilisant ISR on a route with export const prerender = true va have aucun effect, since the route is prerendered at construire temps.” ISR and prerender are alternatives, pas stackable.

adapter-cloudflare — Workers/Pages, global edge

adapter-cloudflare targets Cloudflare Workers and Pages — SSR on a global edge network, souvent the lowest TTFB pour a geographically spread audience. The important constraint is the runtime: Workers run on V8 isolates, pas Node. From the docs: “You can’t use fs in Cloudflare Workers.” Some Node APIs fonctionner seulement behind the nodejs_compat compatibility flag, and même alors prise en charge isn’t one-to-one. Si vous were reading fichiers at requête temps (a redirection map, a données fichier, custom OG-image inputs), que code nécessite a rethink — covered in the edge section ci-dessous.

(The older adapter-cloudflare-workers is deprecated; nouveau projects utiliser adapter-cloudflare, qui handles les deux Workers and Pages. Si you’re on the old un, migrating is the recommended chemin.)

adapter-netlify — functions or Edge Functions (Deno)

adapter-netlify deploys to Netlify’s Node-based functions by par défaut, or to Deno-based Edge Functions with edge: true. Même shape as Vercel: par défaut serverless with an edge opt-in. Un SvelteKit-specific footnote — Netlify Formulaires exiger the form’s page to be prerendered so Netlify peut detect the formulaire markup at deploy temps, qui is a petit “prerender this route” requirement layered on top of the adapter choice.

La décision en une ligne chaque

  • Pure content siteadapter-static, prerender everything.
  • Content site with dynamic pocketsadapter-node/-vercel/-cloudflare, prerender = true on content, false/'auto' on the dynamic routes.
  • App/dashboard with personalization → SSR-first (node or edge), prerender seulement the static shell (marketing, login).
  • Global, TTFB-critical audience → an edge adapter pour the dynamic routes, accepting the Node-API constraints and cold-start reality.

(The Decision Tree tab walks ce as a branching flow.)

Prerendering strategy pour mixed sites

Ce que true / false / 'auto' en réalité do

export const prerender is a per-route (or per-layout) page option, and the three valeurs aren’t simplement on/off:

  • true — construire ce route to static HTML at construire temps. Critically, it’s “excluded from manifests utilisé pour dynamic SSR, making votre serveur (or serverless/edge functions) plus petit.” Une fois prerendered, the route can’t fall back to dynamic rendering — it’s static, complet arrêter.
  • false — toujours render on requête. Aucun static fichier.
  • 'auto' — the mixed-site outil. It prerenders the route and garde it in the dynamic server manifest, so the même route peut be served statically pour connu paths and server-rendered pour the rest. Ce is construit pour exactly the cas the docs décrire: a route comme /blog/[slug] “où vous vouloir to prerender votre la plupart recent/popular content but server-render the long tail.”

Parce que prerendered routes shrink le serveur bundle, a mostly-prerendered site with a few 'auto'/false routes deploys a plus petit, cheaper, faster function — an efficiency win independent of SEO.

Dynamic routes besoin an entries function

The prerender robot d’exploration discovers pages by suivant <a> liens from votre entry points. Que fonctionne pour static routes, but a dynamic route comme /blog/[slug] has aucun fixed URL pour the robot d’exploration to trouver. Si nothing liens to a donné slug, SvelteKit won’t know it exists — and you’ll hit the classic construire error que routes “were marked as prerenderable, but were pas prerendered.”

The fix is an explicit entries function (or config.kit.prerender.entries) que enumerates the parameter valeurs:

// src/routes/blog/[slug]/+page.server.js
export const prerender = true;

export function entries() {
  return [
    { slug: 'hello-world' },
    { slug: 'sveltekit-deployment-seo' },
  ];
}

En pratique vous generate que liste from votre CMS or content directory. Sans it, prerendering seulement covers the slugs the lien robot d’exploration se produit to trouver.

The /blog/[slug] pattern in the wild

Put the two ensemble and vous have the canonical mixed-site setup: prerender = 'auto' plus an entries function que renvoie votre recent and popular posts. Ceux obtenir static HTML at construire temps; anything pas in the liste falls via to SSR on demand. Nouveau posts render dynamically jusqu’à the suivant construire prerenders les. It’s the pragmatic middle ground entre “prerender all 40,000 posts every build” and “render every post on every request.”

Edge runtime constraints que affecter SEO

config.runtime = 'edge' is per-route (on Vercel)

Edge isn’t an all-or-nothing switch. On Vercel it’s a per-route page option:

// +page.server.js or +server.js
export const config = { runtime: 'edge' };

Que signifie vous pouvez push high-traffic, cacheable routes to the edge pour low TTFB pendant que keeping Node-dependent routes on the standard serverless (Node) runtime in the même deployment. Mix deliberately.

Aucun fs, aucun arbitrary Node APIs

The edge runtimes — Cloudflare Workers, Vercel Edge Functions, Netlify’s Deno Edge Functions — don’t provide Node’s fs. Cloudflare’s docs: “Vous pouvez’t utiliser fs in Cloudflare Workers.” Vercel’s: “Vous pouvez’t utiliser fs in edge functions.” Les deux point to the même two escape hatches: utiliser the read helper from $app/server to accès bundled assets, or “prerender the routes in question” so the fichier accès se produit at construire temps au lieu de at requête temps.

The SEO-adjacent cas où ce bites: dynamic OG-image generation que reads a font or template fichier, file-based redirection maps, or a sitemap endpoint que reads content off disk. Quelconque of ceux soit moves to $app/server’s read() or moves to prerender/construire temps. It’s pas a blocker — it’s a “know before you pick edge” constraint.

Cold starts and TTFB — quand edge helps and quand it doesn’t

Edge functions encore cold-start. A cold edge function on its premier requête peut be slower que a warm Node server, and dramatically slower que a prerendered fichier served from cache. Edge wins quand the function stays warm or quand it’s paired with aggressive mise en cache so la plupart requêtes jamais hit the function at tout. It is pas automatically the fastest option — “deploy to the edge” n’est pas a synonym pour “faster.” Pour a content site, prerendered static output beats edge SSR on TTFB every temps, parce que there’s aucun function to commencer.

Generating sitemap.xml and robots.txt (SvelteKit won’t)

Ce is the gap la plupart SvelteKit tutorials skip and la plupart audits catch. SvelteKit generates aucun sitemap.xml and aucun robots.txt automatically — regardless of adapter, regardless of how nombreux pages vous prerender. A entièrement static site with thousands of prerendered pages encore ships with aucun sitemap unless vous construire un.

The +server.js endpoint pattern

The idiomatic sitemap is a route endpoint que renvoie XML with the correct Content-Type:

// 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' },
  });
}

The strategy dépend on votre adapter

Here’s the partie que ties ce whole article ensemble: votre sitemap strategy is downstream of votre adapter choice.

  • On adapter-static, le sitemap endpoint nécessite export const prerender = vrai so it’s inclus in the static output — there’s aucun server at runtime to generate it on requête. It’s baked at construire temps, qui signifie it’s seulement as fresh as votre dernier construire.
  • On a Node/serverless/edge adapter, the même endpoint peut generate le sitemap dynamically per requête from votre CMS or database — toujours current, aucun rebuild nécessaire. (On an edge adapter, remember the fs constraint: pull URLs from an API or binding, pas a disk lire.)

So the “should my sitemap be static or dynamic?” question isn’t a separate decision — it falls out of the adapter vous déjà chose.

robots.txt: static fichier vs. endpoint

Two options. Drop a plain robots.txt in votre static/ folder (served at /robots.txt automatically), qui is the simplest choice and fine pour la plupart sites. Or generate it from a src/routes/robots.txt/+server.js endpoint quand vous besoin it to differ by environment (blocking robots d’exploration on staging, allowing les in production, pour instance). Soit façon, don’t block votre /_app/ bundle or CSS — que breaks rendering pour engines que do render.

Si you’re coming at ce from the broader framework or JavaScript-SEO angle, the “where and when does rendering happen” logic ici is the même logic que governs JavaScript SEO généralement, and the SvelteKit fundamentals piece in ce section covers the rendering modes and metadata patterns ce article builds on top of.

Add an expert note

Pin an expert quote

New person? Create their unclaimed profile at /admin/experts/ → Pin a quote first.