Netlify: env vars not loading in functions [2026 fix]

Netlify functions can't access env vars because they're not deployed in netlify.toml or using legacy config. Deploy with netlify.toml [functions] section.

Netlify: Environment Variables Not Loading in Functions

TL;DR

Cause: Your environment variables are set in Netlify UI but not declared in netlify.toml under [functions], or you're using legacy function deployment without proper config.

Fix: Add environment = { VAR_NAME = "value" } to your netlify.toml [functions] section and redeploy—UI-only vars don't auto-inject into serverless functions.

---

Real Console Error Messages

Here are the exact errors you'll see in your 2am panic:

``` Uncaught ReferenceError: process.env.DATABASE_URL is not defined at handler (/var/task/functions/api.js:5:3) ```

``` Error: STRIPE_SECRET_KEY is undefined. Check your environment configuration. at Object.<anonymous> (functions/payments.js:1) ```

``` TypeError: Cannot read property 'split' of undefined at getApiKey (functions/auth.js:12:3) [process.env.API_KEY is undefined] ```

``` Netlify Functions runtime error: Missing required environment variable MESSAGE: Cannot find API_URL in function scope ```

``` FunctionError: Your function returned an error: TypeError: database_host is undefined CAUSED BY: process.env.database_host was not injected at runtime ```

---

Broken Code vs. Fix

❌ BROKEN: Environment vars in UI only

```javascript // netlify.toml - INCOMPLETE [build] command = "npm run build" functions = "netlify/functions"

// functions/api.js exports.handler = async (event) => { const dbUrl = process.env.DATABASE_URL; // ❌ UNDEFINED - only set in UI const stripe = require('stripe')(process.env.STRIPE_KEY); // ❌ NULL return { statusCode: 500, body: 'Vars not loaded' }; }; ```

✅ FIXED: Declare vars in netlify.toml

```toml

netlify.toml - CORRECT

[build] command = "npm run build" functions = "netlify/functions"

[functions] environment = { DATABASE_URL = "postgresql://user:pass@host/db", STRIPE_KEY = "sk_live_abc123", LOG_LEVEL = "debug" } ```

```javascript // functions/api.js - NOW WORKS exports.handler = async (event) => { const dbUrl = process.env.DATABASE_URL; // ✅ "postgresql://user:pass@host/db" const stripe = require('stripe')(process.env.STRIPE_KEY); // ✅ "sk_live_abc123" return { statusCode: 200, body: JSON.stringify({ status: 'connected' }) }; }; ```

---

Why This Happens

Netlify UI environment variables are available to build processes and site context but not automatically injected into serverless function runtime unless you:

1. Define them in netlify.toml [functions] block (recommended for production) 2. Use Netlify's environment variable injection API (for dynamic values) 3. Set them per-function with individual config files (for granular control)

This is a security boundary: functions get explicit variable allowlists to prevent accidental secret leakage.

---

Step-by-Step Fix

Step 1: Open your netlify.toml at project root

Step 2: Add or update the [functions] section:

```toml [functions] environment = { DATABASE_URL = "your_actual_value", API_KEY = "sk_test_xyz" } ```

Step 3: For sensitive secrets, use Netlify's UI Settings > Build & Deploy > Environment variables, then reference them in netlify.toml:

```toml

Set these in UI, they'll be available as $VAR_NAME in netlify.toml

[functions] environment = { SECRET = "$SECRET_FROM_UI" } ```

Step 4: Commit and push:

```bash git add netlify.toml git commit -m "fix: add env vars to functions config" git push origin main ```

Step 5: Netlify redeploys automatically—check Deployments tab for completion (2-5 min)

Step 6: Test your function:

```bash curl https://your-site.netlify.app/.netlify/functions/api

Should return 200, not undefined errors

```

---

Still Broken? Check These Too

1. Function naming mismatch: File is api.js but you're calling .netlify/functions/api-handler? Netlify function routes match filenames exactly (minus .js). Rename files to match your routes. See [Netlify function routing guide](/?guide=netlify-function-routing).

2. Build-time vs. runtime confusion: Variables used *during build* (Next.js, Gatsby) need NEXT_PUBLIC_* prefix for client exposure. Backend function vars use plain PROCESS_ENV. Don't mix them. Check [environment variable scoping](/?guide=env-var-scope).

3. Stale function cache: Netlify caches old function builds. Clear it by navigating to Deploys > trigger deploy or delete .netlify folder and rebuild locally with netlify build && netlify deploy.

---

Official Docs

[Netlify Functions Environment Variables Documentation](https://docs.netlify.com/functions/overview/#environment-variables)

[netlify.toml Reference](https://docs.netlify.com/configure-builds/file-api-reference/)

---

Version Notes

This fix applies to Netlify Functions on any runtime (Node 18+, Python, Go). Behavior is consistent as of 2026. If you're using Netlify Edge Functions (edge compute), variables are accessed the same way but deployed with different syntax—[check Edge Functions docs](https://docs.netlify.com/edge-functions/overview/) if .netlify/functions isn't your deployment target.

---

Found a different variation? Drop it in the comments—did you hit this with a specific framework (Next.js, Remix, SvelteKit) or a different error message? Let us know so we can expand this guide.

🔥 0d
LIVE
↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising •↑ Resend loved by devs •↓ PlanetScale rage spiking •⚠ Vercel pricing complaints •↑ Railway gaining fast •↑ Supabase happiness rising
DEVELOPER PAIN RADAR // Loading...

Developers complain.
Opportunities appear.

We track what developers are struggling with today — and what opportunities that creates.

—
guides today
—
avg happiness
🔥 Pain
📖 Guides
🔭 Explore
👤 Mine
🔥 Pain Radar — rage scores today
↗ share
💡 Opportunity Feed — pain = market gap
📈 Tool Momentum
all scores →
📖 Latest Guide
all guides →
📖 All Guides
📊 Tool Scores
+ Submit
📰 Hacker News
➕ Submit a Tool
← back