Overview

Astro defaults to static output: every page is prerendered at build time and served as a flat file. SSR mode renders pages on each request, which unlocks personalized content, auth-gated routes, and dynamic data. Astro 5 offers three output modes: static (default), server (all routes SSR by default), and hybrid (all routes prerendered by default, with per-route opt-out). For most sites, hybrid is the right choice because it keeps the static-first benefit and lets you add server endpoints where needed.

Set output: 'hybrid' in astro.config.mjs

hybrid mode prerenderers everything except pages and endpoints that explicitly opt out. This keeps build output minimal and CDN-cacheable for content routes while letting API routes and auth endpoints run on the server.

// astro.config.mjs
import { defineConfig } from "astro/config";
import vercel from "@astrojs/vercel/serverless";
 
export default defineConfig({
  output: "hybrid",
  adapter: vercel(),
});

A page or endpoint opts out of prerendering with one export:

---
export const prerender = false;
---

Without that export in hybrid mode, the page is prerendered at build time. Add it only when the page genuinely needs server rendering.

Use output: 'server' when most routes are dynamic

server mode server-renders all pages by default. Use it when the majority of routes require auth, user-specific data, or real-time reads. Opt individual pages back into static with export const prerender = true.

Avoid server mode on a mostly-content site. Prerendering content pages and serving them from a CDN is faster and cheaper than server-rendering them on every request.

Pick an adapter that matches your deployment target

Adapters translate Astro’s server output to the target runtime. Each adapter is installed once and configured as the adapter in astro.config.mjs.

  • Vercel: @astrojs/vercel/serverless for Lambda or @astrojs/vercel/edge for edge functions. See vercel.
  • Netlify: @astrojs/netlify/functions.
  • Cloudflare Pages: @astrojs/cloudflare. Runs on Cloudflare Workers; the cloudflare: module namespace gives access to KV, D1, and R2. See cloudflare.
  • Node.js: @astrojs/node for self-hosted deployments behind a reverse proxy.

Install with astro add to get the correct config automatically:

npx astro add vercel

Running astro build without an adapter configured while output is not static throws an error at build time.

Access request data in server routes

In SSR mode, Astro.request is a standard Request object. Astro.cookies gives access to cookies. Astro.redirect() performs server-side redirects.

---
export const prerender = false;
 
const session = Astro.cookies.get("session");
if (!session) return Astro.redirect("/login");
 
const data = await fetch(`/api/user/${session.value}`).then(r => r.json());
---
<p>Welcome, {data.name}</p>

Avoid Astro.request in prerendered pages; it is undefined at build time and will throw.

Write API endpoints as .ts files in src/pages/

Server endpoints export GET, POST, PUT, DELETE, or ALL functions. They receive a APIContext argument with request, cookies, params, and locals.

// src/pages/api/user.ts
import type { APIRoute } from "astro";
 
export const GET: APIRoute = async ({ cookies }) => {
  const token = cookies.get("token")?.value;
  if (!token) return new Response("Unauthorized", { status: 401 });
  const user = await lookupUser(token);
  return new Response(JSON.stringify(user), {
    headers: { "Content-Type": "application/json" },
  });
};

In hybrid mode, endpoint files in src/pages/api/ are server-rendered by default when they export a handler function. They do not need export const prerender = false unless the entire project is in static mode.