Skip to content

adapter-cloudflare: wrangler.json vars not available via $env/dynamic/private during dev #15736

Description

@m-dressler

Describe the bug

When using @sveltejs/adapter-cloudflare with a wrangler.json file, environment variables defined under vars are only available via platform.env during local development. They are not bridged into process.env, which means $env/dynamic/private cannot see them.

This creates a split where:

  • vars in wrangler.json → available via platform.env (local + production), invisible to $env/dynamic/private during dev
  • values in .env → available via $env/dynamic/private during dev (Vite loads them into process.env), not available at runtime on Cloudflare

In production on Cloudflare, $env/dynamic/private correctly resolves both vars and secrets from the Worker runtime. The inconsistency is local-dev only, but it forces developers into one of these workarounds:

  1. Duplicate every var in both wrangler.json and .env, keeping them manually in sync.
  2. Abandon $env/dynamic/private and access everything through event.platform.env, losing the ergonomics of SvelteKit's env module and requiring null-safety guards since platform can be undefined.
  3. Write a custom Vite plugin that reads wrangler.json and injects vars into process.env at dev time.

None of these are obvious, and developers hit this wall only after migrating away from $env/static/private (which is the recommended move on Cloudflare Pages — see context below).

Context: why developers end up here

On Cloudflare Pages, adding a wrangler.json to your project causes the dashboard-defined plaintext environment variables to no longer be injected into the build container's process.env. This means $env/static/private breaks at build time for any variable not defined as an encrypted secret.

The natural fix is to:

  1. Move plaintext config into wrangler.json vars (the intended source of truth for runtime config)
  2. Switch from $env/static/private to $env/dynamic/private (since vars are now runtime bindings)
  3. Keep secrets in .env / .dev.vars for local dev and in the Cloudflare dashboard for production

This is the correct architecture — build once, resolve at runtime. But step 2 silently breaks local dev because the adapter doesn't bridge wrangler.json vars into the env system that $env/dynamic/private reads from.

Suggested approach

In the adapter's Vite plugin (or dev hook), read the resolved wrangler config and merge vars into process.env with lower precedence than existing values:

This keeps wrangler.json as the single source of truth for runtime config, .env as the source for local secrets, and $env/dynamic/private works everywhere without duplication or custom plugins.

Reproduction

  1. Create a SvelteKit project with @sveltejs/adapter-cloudflare
  2. Define a var in wrangler.json:
{
  "name": "my-app",
  "pages_build_output_dir": ".svelte-kit/cloudflare",
  "vars": {
    "API_HOST": "https://api.example.com"
  }
}
  1. In a server endpoint or hook, import and log it:
import { env } from '$env/dynamic/private';
console.log(env.API_HOST); // undefined during `vite dev`
  1. Run npm run devenv.API_HOST is undefined
  2. Add API_HOST=https://api.example.com to .env — now it works, but you have the value in two places

Expected behavior

During local dev, $env/dynamic/private should include variables defined in wrangler.json vars. The adapter already reads the wrangler config (via platformProxy.configPath) to populate platform.env — it should also inject those values into process.env so that SvelteKit's own env module works consistently.

Values from .env / .dev.vars should take precedence over wrangler.json vars to allow local secret overrides.

Logs

System Info

@sveltejs/adapter-cloudflare 7.2.8

Severity

annoyance

Additional Information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions