# How to Configure Build-Time vs. Runtime Environment Variables in Astro Projects

> Learn how to configure build-time vs. runtime environment variables in Astro projects. Understand the PUBLIC_ prefix for browser-accessible variables and server-only use cases.

- Repository: [Astro/astro](https://github.com/withastro/astro)
- Tags: how-to-guide
- Published: 2026-03-06

---

**Astro distinguishes build-time from runtime environment variables using the `PUBLIC_` prefix, where variables without the prefix are inlined during the build for server-only use, while `PUBLIC_` prefixed variables remain accessible to the browser and resolve at request time.**

Astro projects rely on environment variables to manage configuration across different deployment stages. In the `withastro/astro` repository, the framework implements a strict separation between **build-time secrets** and **runtime public values** to ensure sensitive data never leaks to the client while allowing dynamic configuration for browser-accessible settings.

## Understanding the Two Variable Types

Astro categorizes environment variables based on when their values are fixed and who can access them:

| Stage | Definition | How to Access | When the Value is Fixed |
|-------|------------|---------------|------------------------|
| **Build-time** | Variables that **do not start with** `PUBLIC_` | `import.meta.env.VARIABLE_NAME` | At **build time** – the value is baked into the static output and cannot change at runtime. |
| **Runtime** | Variables prefixed with **`PUBLIC_`** (e.g., `PUBLIC_API_URL`) | `import.meta.env.PUBLIC_VARIABLE` | At **runtime** – the value is resolved on each request or page load and can change without rebuilding. |

According to the source code in [`packages/astro/src/runtime/server/environment.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/runtime/server/environment.ts), Astro builds the `import.meta.env` proxy by filtering out non-public variables and resolving values from `process.env` at the appropriate stage.

## Configuring Build-Time (Server-Only) Variables

Build-time variables are **inlined** during the compilation process. Astro substitutes the reference with a literal string in the compiled code, making the value unavailable to the browser and unchangeable after deployment.

Create a `.env` file in your project root without the `PUBLIC_` prefix:

```ts
// .env
API_SECRET=mySuperSecretKey

```

Access the variable in server-only code using `import.meta.env`:

```ts
// src/pages/api/secret.ts (server-only route)
export async function get() {
  const secret = import.meta.env.API_SECRET; // value is replaced at build time
  return new Response(`The secret is ${secret}`);
}

```

*The value `mySuperSecretKey` is baked into the built server bundle and never sent to the browser.*

## Configuring Runtime (Public) Variables

Runtime variables are resolved when the code executes rather than when it compiles. Prefix the variable name with `PUBLIC_` to expose it to client-side code:

```ts
// .env
PUBLIC_API_URL=https://api.example.com

```

Use `import.meta.env.PUBLIC_VARIABLE` in components or pages:

```tsx
// src/components/Fetcher.tsx
export default function Fetcher() {
  // Resolved at runtime, can differ between deployments
  const apiUrl = import.meta.env.PUBLIC_API_URL;

  const [data, setData] = useState(null);
  useEffect(() => {
    fetch(`${apiUrl}/data`).then(r => r.json()).then(setData);
  }, [apiUrl]);

  return <pre>{JSON.stringify(data, null, 2)}</pre>;
}

```

Because the variable is prefixed with `PUBLIC_`, Astro injects it into the HTML so the browser can read it. In a server-side context, Astro reads the actual environment at request time; in a client-side context, the value is injected into the compiled output for the browser to access.

## Using Mixed Variables in Server-Side Rendering

You can combine both variable types in `.astro` files. The server resolves build-time variables during the build process and runtime variables on each request:

```astro
---
// src/pages/index.astro
const buildTime = import.meta.env.SITE_TITLE; // baked at build
const runtimeApi = import.meta.env.PUBLIC_API_URL; // resolved on each request
---
<html>
  <head><title>{buildTime}</title></head>
  <body>
    <h1>{buildTime}</h1>
    <p>Fetching from {runtimeApi}...</p>
  </body>
</html>

```

The `SITE_TITLE` value is embedded as a static string during compilation, while `PUBLIC_API_URL` is read from the environment when the server handles the request.

## Summary

- **Use the `PUBLIC_` prefix** only when you need the variable available in the browser; otherwise, it stays server-only.
- **Build-time variables are inlined** into the compiled code and cannot change without rebuilding the site.
- **Runtime variables resolve on each request**, allowing you to update configuration by changing the server environment without triggering a new build.
- The core logic implementing this behavior resides in [`packages/astro/src/runtime/server/environment.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/runtime/server/environment.ts).

## Frequently Asked Questions

### What is the difference between build-time and runtime environment variables in Astro?

Build-time variables are defined without the `PUBLIC_` prefix and are inlined as literal strings during the build process, making them fixed and server-only. Runtime variables use the `PUBLIC_` prefix and are resolved when the code executes, either on the server during request handling or in the browser when the page loads.

### How do I keep sensitive API keys secret in Astro?

Store sensitive keys in your `.env` file **without** the `PUBLIC_` prefix (e.g., `API_SECRET_KEY`) and access them via `import.meta.env.API_SECRET_KEY` only in server-side code. Astro's build process inlines these values and never exposes them to client-side bundles or the browser.

### Can I change environment variables after building an Astro site?

You can change **runtime** variables (those prefixed with `PUBLIC_`) by updating the environment where your server runs, as these are read at request time. However, **build-time** variables are baked into the static output and cannot be modified without rebuilding the site.

### Where does Astro implement the environment variable logic?

The core implementation lives in **[`packages/astro/src/runtime/server/environment.ts`](https://github.com/withastro/astro/blob/main/packages/astro/src/runtime/server/environment.ts)**, which constructs the `import.meta.env` proxy, filters out non-public variables for client bundles, and resolves values from `process.env`. The conventions are documented in **[`docs/en/reference/configuration-reference.md`](https://github.com/withastro/astro/blob/main/docs/en/reference/configuration-reference.md)**.