Guide

NEXT_PUBLIC_ and VITE_ variables are public. Keep secrets out of them.

Not every .env leak needs a misconfigured server. Front-end build tools read .env too, and every variable with a public prefix is shipped to the browser — inlined into the JavaScript bundle or the page itself, where any visitor can read it in DevTools.

Which variables reach the browser

FrameworkPublic prefixRead in client code as
Next.jsNEXT_PUBLIC_process.env.NEXT_PUBLIC_…
Vite (Vue, React, Svelte…)VITE_import.meta.env.VITE_…
Create React AppREACT_APP_process.env.REACT_APP_…
NuxtNUXT_PUBLIC_useRuntimeConfig().public
SvelteKitPUBLIC_$env/static/public
AstroPUBLIC_import.meta.env.PUBLIC_…
GatsbyGATSBY_process.env.GATSBY_…

The prefix is the framework saying “this one is public”. Obfuscation doesn’t change that: if the browser can read a value, so can everyone.

What is safe to expose, and what never is

Check your build

DotenvScan checks your server for config files; it doesn’t read your JavaScript. Search the build output instead, after a production build:

grep -rnoE '\bsk_live_[A-Za-z0-9]{4}|\bAKIA[A-Z0-9]{12}|-----BEGIN [A-Z ]*PRIVATE KEY' .next/static dist build 2>/dev/null

Add the key prefixes of your own providers. On the live site, open DevTools → Sources and search all files (Ctrl+Shift+F, or ⌘⌥F on a Mac) for the same strings.

If a secret shipped

  1. Rotate it. It is in every cached copy of your bundle — browsers, CDNs, web archives. Removing it from the code doesn’t take those back.
  2. Move the call to the server — a Next.js Route Handler or Server Action, a SvelteKit server route, a serverless function — that reads the unprefixed variable and returns only what the page needs.
  3. Guard server-only code. In Next.js, import 'server-only' at the top of a module makes the build fail if client code imports it.
  4. Check the provider’s usage logs for calls you didn’t make.

Server-side .env files leak too — the four usual ways. Check yours with the scanner.

Scan your server for .env files