
Deploying to Vercel can suddenly throw "Invalid API key" or undefined environment variable errors. This guide explains why it happens and provides concrete steps to resolve the issue.
Root Causes of Undefined Env Vars on Vercel
When a Vercel deployment reports process.env.MY_KEY as undefined, the problem is almost always related to how environment variables are stored, built, or referenced. Understanding the underlying mechanisms helps avoid repeated redeploy‑time failures.
Vercel treats environment variables as immutable at build time. A variable must exist in the Vercel project settings for the specific environment (Production, Preview, Development) before the build starts. If the variable is added after a deployment, the existing build artifact will not see it; a new deployment is required.
- .env.local is not deployed – The file is git‑ignored by design, so Vercel never receives its contents. The platform expects a copy of each variable to be entered manually or via the CLI.
- Variable added after the last deployment – Environment variables only apply to subsequent builds. The current deployment continues to use the snapshot taken at build time.
- Wrong environment selection – A variable marked for “Preview” will be missing in a Production deployment, and vice‑versa.
- Missing public prefix – In Next.js, any variable used in client‑side code must be prefixed with
NEXT_PUBLIC_; in Vite it must start withVITE_. Without the prefix the value is stripped from the client bundle. - Typographical errors – Misspelled keys, stray quotes, or trailing whitespace cause the runtime lookup to fail.
Practical examples illustrate each cause:
// Example 1: .env.local (git‑ignored) – never reaches Vercel
// .env.local
SUPABASE_URL=https://xyz.supabase.co
// Fix: add SUPABASE_URL in Vercel Settings → Environment Variables
// Example 2: Missing public prefix in Next.js client code
const url = process.env.SUPABASE_URL; // undefined in browser
// Correct:
const url = process.env.NEXT_PUBLIC_SUPABASE_URL;
// Example 3: Typo in variable name
const key = process.env.OPENAI_API_KET; // typo → undefined
// Correct:
const key = process.env.OPENAI_API_KEY;
To resolve undefined variables, follow a deterministic workflow: add or correct the variable in Vercel’s UI or with vercel env add, verify the environment scope, ensure the proper public prefix for client usage, and trigger a fresh deployment. This approach eliminates the most common root causes without exposing secret keys to the browser.
Adding and Deploying Env Vars in Vercel
Vercel stores environment variables separately from the local .env files that are ignored by Git. Because the values are injected at build time, any change requires a new deployment. The process can be performed through the Vercel UI or the Vercel CLI, and each method allows you to target the three Vercel environments: Production, Preview, and Development.
Adding variables via the Vercel UI
- Open the project in the Vercel dashboard.
- Navigate to Project → Settings → Environment Variables.
- Click Add Variable and enter the
KeyandValue. - Check the boxes for the environments that should receive the variable (Production, Preview, Development).
- Save the entry.
- Trigger a redeploy: go to Deployments → … → Redeploy or push a new commit.
Adding variables via the Vercel CLI
# Add a public variable for production
vercel env add NEXT_PUBLIC_SUPABASE_URL production
# Pull current values into a local .env file (development by default)
vercel env pull .env.local
# Add a secret for all environments
vercel env add OPENAI_API_KEY --secret --environment=preview
After using vercel env add, the CLI prints a confirmation and the variable becomes available only to subsequent builds. To apply the change immediately, run vercel deploy --prod or push a new commit.
Key considerations
- Variables without a public prefix (e.g.,
NEXT_PUBLIC_for Next.js orVITE_for Vite) are only available on the server side. - Public‑prefixed variables are inlined into the client bundle; they must be redeployed to take effect.
- Secret keys (e.g.,
SUPABASE_SERVICE_ROLE_KEY,STRIPE_SECRET_KEY) should never use a public prefix and must remain server‑only. - Typos, trailing spaces, or stray quotes in the value will cause runtime errors such as “Invalid API key”.
- Environment selection determines where the variable is injected: Production for live traffic, Preview for pull‑request builds, Development for local preview builds.
By following these steps, engineers can reliably propagate configuration changes across Vercel environments while ensuring that secret data remains protected and that client‑side variables are correctly prefixed and rebuilt.
Using the Correct Prefixes for Browser Code
When a variable is accessed from browser‑side code, the bundler must replace the reference with a literal value during the build step. Both Next.js and Vite enforce an explicit “public” prefix to signal that a variable is safe to be inlined and shipped to the client.
- Next.js:
NEXT_PUBLIC_prefix - Vite:
VITE_prefix
Only variables that carry one of these prefixes are read from process.env (Next.js) or import.meta.env (Vite) at compile time. The replacement happens before any JavaScript is emitted, which means the final bundle contains the actual string value, not a runtime lookup. Because the value is baked into the static assets, a new deployment is required whenever the variable changes.
Code examples
// Next.js – client component
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!;
const supabaseKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
// Vite – any module
const apiBase = import.meta.env.VITE_API_BASE_URL;
const apiKey = import.meta.env.VITE_PUBLIC_API_KEY;
Attempting to read a variable without the required prefix results in undefined at runtime, because the bundler excludes it from the client bundle. For example, process.env.SUPABASE_URL in a Next.js page will never be replaced and will remain undefined after deployment.
Why the prefix matters
- It marks the variable as public, allowing the build tool to inline it safely.
- Inlined values are part of the static JavaScript sent to every visitor, so secret keys must never use these prefixes.
- Because the value is baked at build time, updating the variable in the hosting platform (e.g., Vercel) requires a fresh build and redeploy.
Best practice is to keep secret credentials (e.g., SUPABASE_SERVICE_ROLE_KEY, OPENAI_API_KEY) on the server only, accessed via API routes, server actions, or route handlers. Client‑side code should reference only the explicitly prefixed variables, and developers should verify that the environment is correctly configured and redeployed after any change.
Keeping Secret Keys Server‑Side Only
Secret keys such as SUPABASE_SERVICE_ROLE_KEY, OPENAI_API_KEY, and STRIPE_SECRET_KEY grant full access to backend services. When these credentials are bundled into client‑side JavaScript they become visible to every visitor, allowing an attacker to invoke privileged APIs, bypass rate limits, or exfiltrate data. Compliance frameworks—including SOC 2, ISO 27001, NIST SP 800‑53, and the OWASP Application Security Verification Standard—require that authentication secrets be stored and used only in a trusted execution environment. Violating this principle creates a direct control‑plane exposure that most audits flag as a high‑severity finding.
In modern JavaScript frameworks the build process inlines any environment variable that carries a public prefix (NEXT_PUBLIC_ for Next.js, VITE_ for Vite). As the evidence notes, “Anything with NEXT_PUBLIC_ or VITE_ ends up in the JavaScript sent to every visitor.” Therefore, secret keys must be defined without those prefixes and accessed only in server‑only modules such as route handlers, server actions, or API routes.
Practical guidelines
- Define secrets without public prefixes. In Vercel or similar platforms add the variables directly (e.g.,
SUPABASE_SERVICE_ROLE_KEY) and mark them for the required environments. - Restrict usage to server‑side code. Import
process.envonly inside files that run on the server (e.g.,app/api/chat/route.ts). - Fail fast on missing values. Use a helper that throws a clear error if a variable is undefined.
- Never expose keys in client bundles. Do not reference secret keys in components, pages, or any code that is compiled for the browser.
- Audit and rotate regularly. Follow the key‑rotation procedures required by your compliance framework.
Example of a safe server‑only handler in a Next.js 13 app:
// app/api/chat/route.ts
export async function POST(req: Request) {
const openAiKey = requireEnv('OPENAI_API_KEY'); // server‑only
const body = await req.json();
// Call OpenAI with the secret key – never sent to the client
const response = await fetch('https://api.openai.com/v1/chat/completions', {
method: 'POST',
headers: {
'Authorization': `Bearer ${openAiKey}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
return new Response(await response.text(), { status: response.status });
}
function requireEnv(name: string) {
const value = process.env[name];
if (!value) throw new Error(`Missing env var: ${name}`);
return value;
}
By keeping secret keys strictly on the server, you protect the integrity of your services, satisfy regulatory requirements, and reduce the attack surface exposed to end users.
Fail‑Fast Validation and Clear Error Messages
Fail‑fast validation of environment variables prevents runtime surprises that are common when a build runs in a different execution context than the developer’s machine. In server‑side Node.js or edge runtimes, the process environment is available only at start‑up, so a missing key should abort early with a clear, actionable message.
/**
* Throws if the requested variable is undefined, empty, or only whitespace.
* Use this at the top of your entry point or in a dedicated config module.
*/
function requireEnv(name: string): string {
const value = process.env[name];
if (!value || /^\s*$/.test(value)) {
throw new Error(`Missing required environment variable: ${name}`);
}
return value;
}
// Example usage
const SUPABASE_URL = requireEnv('SUPABASE_URL');
const OPENAI_API_KEY = requireEnv('OPENAI_API_KEY');
When the function throws, the stack trace points directly to the missing variable, allowing developers to add the key in the CI/CD system (e.g., Vercel Settings → Environment Variables) and redeploy. This approach satisfies compliance frameworks such as NIST and ISO 27001, which require that secrets be validated before code execution.
Why dynamic lookups break in client‑side code
Frameworks that bundle JavaScript for the browser (Next.js, Vite, etc.) replace static references to process.env.VAR_NAME or import.meta.env.VAR_NAME at build time. The bundler cannot inline a value when the variable name is computed at runtime, so constructs like process.env[name] remain as literal property accesses in the shipped bundle. Because the browser never receives the full process.env object, the lookup resolves to undefined, producing errors such as “Invalid API key” or “supabaseUrl is required”.
- Static inlining required: Only variables with a known identifier are replaced.
- Public prefix needed: Variables intended for the client must be prefixed (e.g.,
NEXT_PUBLIC_orVITE_) so the build tool knows to expose them. - Redeploy after change: Adding or updating a prefixed variable requires a new build; the value is baked into the JavaScript bundle.
- Security boundary: Secrets without the public prefix stay server‑only, preventing accidental exposure.
Therefore, the recommended pattern is:
- Declare required server‑side variables and validate them with
requireEnvat start‑up. - Expose only safe, public variables using the framework‑specific prefix.
- Reference those public variables directly (e.g.,
process.env.NEXT_PUBLIC_SUPABASE_URL) rather than via dynamic lookups.
Final Checklist & FAQ
When a deployment fails with “Invalid API key” or an undefined environment variable, the root cause is almost always a mismatch between the local development configuration and the runtime environment. The following checklist isolates the most common mis‑configurations and provides concrete steps to verify each one.
Troubleshooting checklist
- Confirm the variable exists in the Vercel dashboard and is marked for the correct environment (Production, Preview, or Development).
- Trigger a fresh build after any change; public prefixes (e.g.,
NEXT_PUBLIC_orVITE_) are baked at build time, so a redeploy is required. - Copy the key directly from the provider console; then remove surrounding quotes, trailing spaces, or newline characters.
- Verify you are using the appropriate key type: client‑side code must reference the public/anon key, never a service‑role or secret key.
- Run a guard clause at startup to fail fast if a required variable is missing:
function requireEnv(name: string) { const v = process.env[name]; if (!v) throw new Error(`Missing env var: ${name}`); return v; }
Common pitfalls
In Next.js, a variable without the NEXT_PUBLIC_ prefix is stripped from the client bundle, resulting in undefined at runtime. Vite uses the VITE_ prefix for the same purpose. Secret keys such as OPENAI_API_KEY or SUPABASE_SERVICE_ROLE_KEY must stay server‑only—store them without a public prefix and reference them inside API routes, Route Handlers, or Server Actions.
FAQ
- Is the Supabase anon (public) key safe to expose?
- Yes, it is intended for client use, but security relies on Row‑Level Security (RLS) policies being enabled on every table. Without RLS, the key can read or write unrestricted data.
- Why does redeploying sometimes fix the issue?
- Public‑prefixed variables are inlined during the build step. Changing their value in the dashboard does not affect an already built bundle; a new build incorporates the updated value.
- What if the key works locally but fails in production?
- Check that the variable is defined for the production environment in Vercel, that the correct prefix is used, and that the value contains no stray whitespace or quotation marks.
Looking for Custom Software or AI Solutions?
Appworks Technologies designs, builds, and scales production enterprise platforms, microservices, and AI agent workflows tailored to your business goals.
Editorial Policy & Research Methodology
Our findings are based on rigorous internal research, verified industry benchmarks, and direct technical implementation experience from our enterprise client projects. All statistics and technical claims are reviewed by senior engineers before publication to ensure accuracy, transparency, and helpfulness for our readers.
