Search the client build output for secret names
Grep the folder your build writes browser JavaScript to. Search for the names in your env files and the key prefixes your providers use. A match in the client output means that text shipped to browsers.
grep -rlE 'API_KEY|SECRET' path/to/client-build
Docs: nextjs.org →
List variables that carry a public prefix
Look through your env files for names starting with NEXT_PUBLIC_ or VITE_. Vite exposes VITE_ variables in client-side source code, and Next.js can inline NEXT_PUBLIC_ values into the browser bundle, so a key in that list may be exposed. To confirm, search the build output for the key's value.
grep -rnE '^(NEXT_PUBLIC|VITE)_' .env*
Docs: vite.dev →
Trace the import that pulled the module in
Open the file that contains the match and find which module imports it. Follow the chain up to a file marked "use client", because Next.js includes that file's imports in the client bundle.
Docs: nextjs.org →
Mark the server config module server-only
Add import 'server-only' at the top of the module that holds secret names and formats. A client import then fails the build instead of shipping. Next.js treats installing the package as optional, but you can install it if your lint rules flag extraneous dependencies.
import 'server-only'
export const config = {
apiKey: process.env.API_KEY,
}
Docs: nextjs.org →
Split public config from secrets
Put the values the browser needs in their own module, built from NEXT_PUBLIC_ variables. Read secrets in one server-side layer and return the fields a component needs.
// lib/public-config.ts: safe for client imports
export const publicConfig = {
analyticsId: process.env.NEXT_PUBLIC_ANALYTICS_ID,
}
Docs: nextjs.org →
In Vite, drop the prefix and call the provider from a backend
Rename the variable without the VITE_ prefix and stop reading it in browser code. Have the browser call a backend server or a serverless/edge function that holds the key and talks to the provider.
console.log(import.meta.env.VITE_SOME_KEY) // "123", reaches the client
console.log(import.meta.env.DB_PASSWORD) // undefined
Docs: vite.dev →
Revoke the exposed key, then rebuild
Revoke the key that shipped, issue a new one and set it on the server. Next.js fixes NEXT_PUBLIC_ values at build time, so rebuild and redeploy to replace the old output. Add secrets detection to your repository tooling to catch a repeat.
Docs: cheatsheetseries.owasp.org →