---
title: Astro
description: The build fetches your public policy once and bundles it, so the
  server never fetches it at runtime.
lastModified: "2026-10-10T14:59:21+01:00"
lastAuthor: Kaylee
---
The build fetches your public policy once and bundles it, so the server never
fetches it at runtime.

`manifest()`, the default mode, bundles the manifest. The integration fetches
it when `astro build` or `astro dev` starts, then embeds it in its generated
server options. The middleware and the consent route use that same snapshot.
The browser options omit it; only `manifest({ resolve: 'browser' })` serves
it to the browser, as the prerendered `/api/c15t/manifest`. No
generated file or extra import is needed. `astro check` and `astro sync` do
not fetch, so type checks work without the backend. `astro preview` serves the
completed build and never fetches a new snapshot.

The build fetches only from an absolute upstream backend URL or
`manifestURL`, because it cannot fetch a route in the app it is still
compiling. `c15t()` without a `backendURL` reads `PUBLIC_C15T_BACKEND_URL`, then
`PUBLIC_INTH_PROJECT_URL`.
With a relative URL, `manifest({ source: 'runtime' })`,
`manifest({ snapshot })`, `hosted()` or `offline()`, the integration skips
the fetch. If the fetch fails, or no backend URL is set, `astro build` stops.
`astro dev` logs a warning instead, and the server fetches and caches the
policy at runtime. The config is read once at build time, so the built server
keeps the backend URL and the snapshot it was built with.

To let a build continue when the snapshot can't be fetched, set
`onBuildError: 'runtime'`, or build with `C15T_ON_BUILD_ERROR=runtime`:

```js title="astro.config.mjs"
c15t({ onBuildError: 'runtime' });
```

`onBuildError: 'fail'` stops `astro dev` too, and stops the build on a
relative URL.

The snapshot is fixed at build time:

* Rebuild after changing policies, translations or vendors. If your CI caches
  build output, force a fresh build.
* Consent choices still go to the backend.

The build reads the backend URL from your public backend URL variable when
the config doesn't pass one: `NEXT_PUBLIC_C15T_BACKEND_URL` in Next.js,
`NUXT_PUBLIC_C15T_BACKEND_URL` in Nuxt, `PUBLIC_C15T_BACKEND_URL` in Astro,
Svelte and SvelteKit, and `VITE_C15T_BACKEND_URL` in TanStack Start and other
Vite apps. Each also reads the matching Inth variable, such as
`NEXT_PUBLIC_INTH_PROJECT_URL`, when the c15t one is unset. See
[set the backend URL](/docs/concepts/modes#set-the-backend-url).

The fetch waits at most 10 seconds. When it fails, or no backend URL is set,
every framework does the same thing:

|Command|Default when the fetch fails|
|--|--|
|Production build: `next build`, `vite build`, `nuxt build`, `astro build`|The build stops with an error.|
|Dev: `next dev`, `vite dev`, `nuxt dev`, `astro dev`|A warning, and the server fetches the policy at runtime.|

Set `onBuildError` to use one behaviour for both. `'fail'` stops dev too.
`'runtime'` lets a production build finish, and the server fetches the policy
at runtime. The `C15T_ON_BUILD_ERROR` environment variable overrides the
option, so you can deploy during a backend outage without a code change:

```sh
C15T_ON_BUILD_ERROR=runtime npm run build
```

Turborepo's strict environment mode hides undeclared variables from tasks, so
list `C15T_ON_BUILD_ERROR` in the build task's `passThroughEnv` there.

The build skips the fetch, without an error, when it can't use a snapshot,
for example when the backend URL is relative. With `onBuildError: 'fail'`, a
relative URL stops the build. [Consent modes](/docs/concepts/modes#what-happens-when-the-download-fails)
lists every case.

For policy updates without a rebuild, set `mode: manifest({ source: 'runtime' })`.
The server then fetches and caches the policy at runtime.
