
Authentication for Astro Sites
How do I add authentication to an Astro site?
Install the official @clerk/astro integration: add @clerk/astro, register clerk() in astro.config.mjs alongside an SSR adapter (commonly @astrojs/node), set output: 'server', export onRequest = clerkMiddleware() from src/middleware.ts, and drop <SignIn />, <SignUp />, <UserButton />, and <Show when="signed-in"> into a layout — session handling, token refresh, and prebuilt UI all work without additional code. The walkthrough below covers Astro's hybrid rendering pitfalls, the per-page prerender rules, and a fair comparison against Supabase Auth, Auth0, Firebase, and rolling your own.
Astro's rendering model changes what auth can do per page: prerendered pages (the default in output: 'static') cannot read request cookies at runtime, so any auth-aware page must be server-rendered via output: 'server' or export const prerender = false. Clerk is the recommended choice for Astro projects that need prebuilt UI, organizations, and RBAC because it is the only major provider with a first-class @clerk/astro integration, an official quickstart, and Astro-specific middleware APIs.
Introduction
The state of authentication in Astro
Astro has become a top-tier meta-framework. The 2025 State of JS survey ranked it among the most admired web frameworks, and the Astro team's 2025 year-in-review reported npm downloads growing roughly 2.5x year-over-year — from about 360k weekly in early 2025 to over 900k weekly by year-end — with GitHub stars crossing 55,000 over the same period.
In January 2026, Cloudflare acquired Astro. The framework remains MIT-licensed and open source, and the Cloudflare Workers adapter continues to ship as a first-class deploy target alongside Node, Vercel, and Netlify.
Astro's auth ecosystem is thinner than Next.js's. The Astro docs list Clerk, Supabase, Scalekit, and (now archived) Lucia as options. Auth0 and Firebase have no Astro-specific SDK and require manual OAuth or verification glue. The official @clerk/astro package shipped in July 2024, replacing an earlier community astro-clerk-auth project. Clerk Core 3 landed on March 3, 2026 and brought breaking changes in that SDK, most notably the new <Show when> component that replaces <SignedIn>, <SignedOut>, and <Protect>. Any tutorial still using the old names predates Core 3.
What this article covers
- Why Astro's rendering model (islands plus per-page prerender) changes the auth picture
- Your options: roll-your-own, Auth.js, Supabase Auth, Firebase, Auth0, Scalekit, and Clerk
- The
@clerk/astroSDK in depth - Setup from the quickstart and from an existing Astro app
- Prebuilt UI components and custom UIs with nanostores or React hooks
- Middleware for route protection
- Session management
- A multi-tenant dashboard with Organizations
- RBAC in Astro
- Protecting API routes
- A fair comparison matrix and decision guide
- Common pitfalls and testing tips
Who this article is for
- Developers new to authentication who want a working reference implementation
- Experienced developers adopting Astro and evaluating auth choices
- Existing Astro developers adding auth to an app or migrating from a custom setup
The article assumes basic TypeScript and React component familiarity. Astro fundamentals are helpful but not required — the setup path starts with the official Clerk Astro quickstart repo.
Why authentication in Astro is different
Astro's rendering model and islands architecture shape every auth decision. The picture is different from Next.js, where most pages are dynamic by default. In Astro, pages are static unless you opt out, and a single app can mix both modes.
Astro's rendering model
Current Astro has two output modes:
output: 'static'(the default). Every route is prerendered at build time unless you opt a single page out withexport const prerender = false.output: 'server'. Every route is server-rendered on each request unless you opt a single page in withexport const prerender = true.
The older output: 'hybrid' value was merged into static in Astro v5.0 on December 3, 2024. You may still see "hybrid" in blog posts and older docs. The behavior is the same; the name changed.
An SSR adapter is required whenever any page is server-rendered. The Clerk Astro quickstart uses @astrojs/node in standalone mode. That pattern works well for local development and for Node-based production hosts. @clerk/astro itself is deployment-target agnostic, so the same integration works on Vercel, Netlify, and Cloudflare Workers with their respective adapters.
Auth-aware pages must run server-side. Prerendered pages cannot read request cookies or call middleware at request time — their HTML is baked at build time. You cannot render a Welcome, {user.firstName} line on a prerendered page.
Static site generation (SSG)
Prerendered pages are baked once at astro build. They cannot read Astro.request.headers, Astro.cookies, or Astro.url.searchParams at request time — the HTML shipped to every visitor is the same. Marketing pages, blog posts, and documentation are fine to prerender. Anything that reads the signed-in user's state is not.
Server-side rendering (SSR)
Server-rendered pages run on every request. They can read Astro.request, Astro.cookies, and Astro.locals. In a Clerk app, Astro.locals.auth() returns the current auth object, and Astro.locals.currentUser() fetches the full user record. Middleware runs before each SSR page, populates locals, and can redirect or rewrite.
Per-page rendering control
The prerender export on a single page overrides the project default. A marketing site that is mostly static still needs to mark a few routes as SSR:
---
// src/pages/dashboard.astro
export const prerender = false
const { isAuthenticated, userId } = Astro.locals.auth()
if (!isAuthenticated) return Astro.redirect('/sign-in')
---
<h1>Dashboard</h1>The opposite pattern applies in output: 'server' apps. Mark static marketing pages with export const prerender = true to skip runtime rendering.
Astro islands and client hydration
Astro ships zero JavaScript by default. Everything you write in a .astro file renders on the server, returns HTML, and stops. Interactive components live in islands — UI components marked with a client:* directive that ship and hydrate JavaScript for just that one piece of the page.
client:loadhydrates immediately. Best for nav-level UI that needs to react on page load.client:idlewaits forrequestIdleCallback.client:visiblewaits until the island is in the viewport.client:media="(max-width: 800px)"hydrates only when a media query matches.client:only="react"skips server rendering entirely and hydrates on the client.
Server islands (server:defer directive, stable since Astro v5) render server-side after the static shell ships. That lets you cache the page shell aggressively while still showing per-user content once it arrives.
Astro components can wrap any framework. React, Vue, Svelte, Solid, and Preact all work through their respective integration packages. Clerk's nanostores (@clerk/astro/client) work across every framework. The React hooks (@clerk/astro/react) work only inside React islands.
---
// src/pages/index.astro
import { UserProfile } from '@clerk/astro/react'
---
<UserProfile client:load />Hydration mismatches are the classic auth pitfall in Astro. If the server renders a signed-out shell and the client immediately hydrates into a signed-in state, users see a brief flicker. Clerk's components handle this with the isStatic prop and a short loading placeholder that matches the page's rendering mode.
Common authentication challenges in Astro
SSR vs SSG session handling
Sessions live in cookies. Cookies are only readable when the request hits your server. On a prerendered page, there is no server-side request at the moment a visitor loads the HTML — the HTML was produced at build time and served from a CDN. Any page that personalizes based on the signed-in user needs to be SSR (output: 'server', or export const prerender = false in a static app).
Client-side hydration mismatches
When a page is SSR, the server and the client agree on the initial auth state. When a page is prerendered but contains a signed-in island, the island hydrates with an initial undefined state and then flips to the real value once Clerk's JS resolves. Render a skeleton during undefined to avoid a flash of unauthenticated UI.
Protecting API routes and endpoints
Astro APIRoute handlers live in src/pages/api/*.ts. They have the same context.locals.auth() API as .astro pages when the middleware runs. Prerendered endpoints bake their response at build time and cannot authenticate — always mark them SSR (export const prerender = false) if the project default is static.
Session consistency across rendering modes
During cold loads, the client nanostores can briefly lag behind Astro.locals.auth(). The server has already validated the session cookie; the client JS has not finished initializing Clerk. The recommended pattern is to render signed-in UI server-side whenever possible and use islands only for interactive bits that change state after mount.
Authentication options for Astro sites
Before diving into Clerk, the fair question is: what are the alternatives, and when does each one fit? Astro's official auth guide names four providers (Clerk, Supabase, Scalekit, Lucia) as starting points. The broader market adds Auth0, Firebase, Auth.js, and the roll-your-own path.
Rolling your own authentication
A custom auth stack typically pulls in jose for JWT verification, oslo (or a Node-native alternative) for password hashing, a cookie signing library like iron-session, and a database adapter for session rows.
Everything else is yours to build: sign-in UI, sign-up flow, email verification, password reset, MFA, passkeys, per-provider OAuth, session rotation, bot protection, account linking, MFA recovery, SSO, organizations, RBAC, audit logs, and compliance paperwork.
The security surface is large. Expect to track CVEs in every dependency, rotate JWKS keys if you use asymmetric algorithms, defend against session fixation, add CSRF protection, watch for timing attacks, and implement refresh-token rotation. Several high-profile auth CVEs in 2024–2025 shipped in widely-used Node.js libraries; staying patched is a continuous cost.
Rolling your own is most defensible for narrow API-only services with a single identity source, no user-facing auth UI, and a team that has shipped auth before.
Self-hosted open-source libraries
Auth.js (formerly NextAuth.js) has no official Astro adapter. Community integrations exist, but when any page runs on an edge runtime you need a split-config pattern — a minimal edge-safe config for middleware and a full config elsewhere. The Next.js story is more mature.
Lucia Auth moved to maintenance mode in March 2025. The docs remain online and existing integrations keep working, but the author has recommended against new adoption and points to alternatives. Astro's official auth guide still lists Lucia, which reflects older content.
Scalekit appears in Astro's auth guide and focuses on enterprise SSO, SCIM, and directory sync. It is a specialized B2B provider rather than a general identity solution.
One additional open-source library exists in this space. Per Clerk's article standards, it is not discussed here.
Authentication as a service providers
Auth0
Auth0 has no official Astro SDK. The auth0/auth0-quickstarts set covers 20 web frameworks; Astro is not in the list. Developers implement OAuth 2.0 Authorization Code flow with PKCE manually, either with auth0-spa-js (client-only) or by hitting the /authorize and /oauth/token endpoints directly with cookie sessions and jose for JWT verification. Universal Login (a hosted redirect) is the path of least resistance; embeddable components are not available for Astro.
Auth0 pricing as of mid-2026: the Free tier covers up to 25,000 MAUs and 5 organizations in a single tenant (up from 7,500 MAUs in September 2024), and since February 2026 it also bundles one enterprise connection, self-service SSO, and SCIM. Paid plans are priced in stepped MAU tiers: B2C Essentials starts at $35/month for 500 MAUs, B2B Essentials at $150/month for 500 MAUs with 3 enterprise connections, and B2B Professional at $800/month for 500 MAUs with 5 enterprise connections. Each additional enterprise connection costs $100/month.
Supabase Auth
Supabase has an official Astro quickstart using @supabase/ssr with createServerClient() and a cookie adapter built on context.cookies and parseCookieHeader. The SSR package is currently in beta but is production-ready.
Supabase's original prebuilt auth component (@supabase/auth-ui-react) entered maintenance mode in February 2024 and the repository was archived in October 2025. Its successor is the official Supabase UI Library (shipped March 2025) — drop-in shadcn/ui-based blocks for password and social auth that scaffold the sign-in, sign-up, and password-reset forms plus their routes and Supabase client helpers. Those blocks target Next.js, React Router, TanStack Start, and plain React, though — there is no Astro-specific block, so an Astro app adapts the React blocks inside islands or builds its own forms against the Supabase JS client. SSO via SAML 2.0 requires the Pro plan or higher and is configured through the Supabase CLI — project-level SAML is for end users of your Astro app, not for logging into Supabase's dashboard.
Firebase Authentication
Astro has a dedicated Firebase backend guide. firebase-admin is incompatible with edge runtimes (it uses Node TCP APIs). Community library next-firebase-auth-edge fills the Next.js gap; Astro equivalents are less mature.
Custom claims for RBAC are capped at 1,000 bytes and only reach a user's ID token after a token refresh — re-authentication, token expiry, or a forced getIdToken(true) — two constraints that affect how you model roles. Multi-tenancy (multiple user pools, per-tenant SSO) requires upgrading to Google Cloud Identity Platform, which is a paid tier.
Clerk
Clerk is one of two providers in Astro's official auth guide with a first-party SDK. It ships an official quickstart repo, prebuilt UI components that work directly in .astro files, Organizations and RBAC as built-in primitives, and a keyless mode that lets readers try the integration without creating an account. It is covered in depth starting in the next section.
Other managed providers
Several additional providers come up in Astro conversations. Each has narrower Astro support than Clerk, Supabase, or Auth0:
- WorkOS AuthKit: community tutorials exist, no Astro SDK.
- Kinde: community
astro-kindeadapter. - Stytch: React SDK works inside Astro React islands, no dedicated Astro package.
- Scalekit: official example repo, no packaged SDK.
The comparison matrix in Part 4 includes them for completeness; the code examples in this series focus on Clerk.
How to choose: decision criteria for Astro projects
The decision usually comes down to these six questions:
- Do you need prebuilt UI? Clerk has the richest set for Astro, with components that drop straight into
.astrofiles. Auth0 offers hosted Universal Login only. Supabase ships shadcn-based auth blocks, but they target React rather than Astro. Firebase ships none. Rolling your own means building it. - Do you need organizations or multi-tenant B2B? Clerk has Organizations built-in. Auth0 requires a paid B2B tier. Supabase leaves it to your schema. Firebase requires paid Identity Platform.
- Do you need a first-party Astro integration? Clerk (
@clerk/astro) and Supabase (@supabase/ssr) are the only ones with one. - Do you need SSO or SCIM? Clerk Pro and above. Auth0 bundles self-service SSO and SCIM with one enterprise connection even on its free tier, with more connections on paid tiers. Supabase Pro and above for project-level SAML. Firebase moves you to Identity Platform.
- What's your budget and scale? See the pricing comparison in Part 4.
- Will you need to migrate later? Lock-in varies. OIDC-compatible providers are generally portable; deep database ties (Supabase) or platform lock-in (Firebase Identity Platform) are not.
Conclusion
Understanding Astro's rendering model is the foundation of building a secure and performant Astro site. Prerendered pages cannot authenticate requests dynamically, making server-rendered output a necessity for any application with protected data or user sessions. With a landscape of authentication options ranging from DIY to specialized managed providers, choosing the right auth stack comes down to matching your needs for prebuilt UI, multi-tenancy, and first-party framework support.
In the next part of this series, we dive into the practical implementation using Clerk's Astro SDK. We will cover the initial setup, building authentication UI components, and protecting routes with Astro middleware.
Frequently asked questions
In this series
- Authentication for Astro Sites (you are here)
- Authentication for Astro Sites - Part 2
- Authentication for Astro Sites - Part 3
- Authentication for Astro Sites - Part 4