There’s a common misconception that your .env file acts like a private vault
inside a Single Page Application (SPA). In reality, anything you inject into
client-side environment variables is basically just a public constant baked in
during the build process.
I see many teams still treating frontend variables as if they’re top-secret. But once the app is built and sent to the browser, anyone with DevTools can see exactly what’s inside. If you're relying on environment variables to hide highly sensitive API keys, you're leaving a real security gap open.
To keep things straight, it's better to split variables into three categories. First, public configuration—things like API URLs or feature flags that don't really matter if people see them. Second, server-side secrets that belong strictly in your backend or CI/CD, like database credentials. Third, build-time secrets, such as npm tokens used only during installation.
In ecosystems like Vite, there’s a safeguard using the VITE_ prefix. Any
variable without that prefix won't make it into your client code. But keep in
mind, this isn't encryption; it’s just a safety net to prevent accidental leaks.
If you force a private key into a VITE_ variable "just for a bit," that key
will end up in production and eventually leak.
A few habits can make managing your app a lot cleaner. For one, don't scatter
import.meta.env all over your project. It’s much better to create a single
module that acts as the one entry point for all client configurations. That way,
if something changes, you aren't hunting through the entire codebase.
Also, if you follow a build-once, deploy-many approach, you can't rely on variables that are "hardcoded" during the build. You'll need runtime configuration. Remember, the whole point of this pattern is to make your builds portable across environments (like moving from staging to production), not to add a layer of secrecy.
Another effective move is using the BFF (Backend For Frontend) pattern. If a third-party service requires a highly sensitive key—like Stripe or OpenAI—your frontend shouldn't be holding it. Let the backend handle the heavy lifting, and just have your frontend talk to your own backend.
At the end of the day, we need to know what can be exposed on the client and
what must stay on the server. Don't feel safe just because your .env file is
in .gitignore. Ultimately, what you’re shipping to the user is just JavaScript
files that anyone can read.