Exposed API Keys on Netlify
Netlify injects environment variables at build time, and any variable accessed in client-side code is embedded in the static JavaScript bundle served from Netlify's CDN. Developers frequently expose secrets by referencing server-intended variables in frontend code.
Scan Your Netlify AppHow It Happens
Netlify makes all environment variables available during the build process. Frameworks like Gatsby, Create React App, and Vite embed referenced variables into the client bundle at build time. Gatsby exposes variables prefixed with GATSBY_, CRA exposes REACT_APP_, and Vite exposes VITE_. Any secret stored in these prefixed variables ends up in the browser. Unlike server-rendered frameworks where some code runs only on the server, Netlify's primary use case is static site deployment. All JavaScript ships to the client. Even Netlify Functions (serverless) are separate from the static site, so developers who put API calls in their static site code expose the keys. Netlify's build logs are another exposure vector. By default, environment variables are printed in build logs during the build process. If the repository is public or build logs are accessible, secrets are visible there too.
Impact
Secrets embedded in static bundles on Netlify's CDN are globally cached and served to every visitor. Even after the variable is removed and the site is redeployed, the old bundle may persist in CDN edge caches and browser caches. Exposed API keys for services like Stripe, OpenAI, or database providers give attackers direct access to those services. For paid APIs, this translates to immediate financial loss. For databases, it means unauthorized data access. Netlify deploy previews add additional risk. Every pull request gets a preview URL with its own build, and preview builds may use different (sometimes more permissive) environment variables than production.
How to Detect
View your Netlify site's source code in the browser. Search the JavaScript bundles for common secret patterns: sk- (Stripe/OpenAI), mongodb+srv://, postgres://, and framework-specific prefixes (GATSBY_, REACT_APP_, VITE_) followed by secret-looking values. Check your Netlify project's environment variable settings. Verify which variables are used by client-side code and ensure none contain secrets. Review build logs for any printed variable values. Vibe App Scanner analyzes the deployed static bundle on Netlify's CDN and identifies API keys using pattern matching and entropy analysis, catching exposed secrets regardless of the framework used.
How to Fix
Move all secret API calls to Netlify Functions (serverless functions in the netlify/functions directory). The static frontend calls your Netlify Function, which holds the secret and proxies the request to the external API. Remove secret values from any environment variable with a GATSBY_, REACT_APP_, or VITE_ prefix. These prefixes are specifically designed to expose variables to the client. Use non-prefixed variables that are only accessible in Netlify Functions. Disable build log output for sensitive variables using Netlify's sensitive variable feature. Mark any environment variable containing a secret as "sensitive" in the Netlify dashboard to redact it from build logs. Rotate all keys that have been in client-side bundles. Redeploy to purge CDN caches and invalidate old bundles.
Code Examples
API calls in a Netlify static site
// Client-side code in a Gatsby/React site on Netlify
const stripe = new Stripe(
process.env.GATSBY_STRIPE_SECRET_KEY // Exposed in bundle!
)// netlify/functions/create-checkout.ts
import Stripe from 'stripe'
export async function handler(event) {
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY) // Server only
const session = await stripe.checkout.sessions.create({
/* ... */
})
return { statusCode: 200, body: JSON.stringify(session) }
}
// Client-side: call the Netlify Function
const res = await fetch('/.netlify/functions/create-checkout',
{ method: 'POST', body: JSON.stringify({ priceId }) })Frequently Asked Questions
Are all Netlify environment variables exposed to the client?
Only variables referenced in client-side code are bundled. However, framework prefixes (GATSBY_, REACT_APP_, VITE_) specifically expose variables to the client build. Variables without these prefixes are available during the build but not embedded unless your code explicitly references them.
How do Netlify Functions keep secrets safe?
Netlify Functions are serverless functions that run on AWS Lambda, separate from the static site. They can access environment variables that are not exposed to the client. The static site calls the function via HTTP, and the function holds the secret key.
Are Netlify deploy preview secrets the same as production?
By default, yes. Netlify uses the same environment variables for deploy previews as production unless you configure context-specific values. Review your netlify.toml or dashboard settings to ensure preview builds don't expose different secrets.
Related Security Resources
Is Your App Vulnerable?
VAS automatically scans for exposed api keys and other security issues in Netlify apps. Get actionable results with step-by-step fixes.
Scans from $5, results in minutes.