Fix guide · high · exposed_env_staging
.env.staging file deployed to production
A /.env.staging file is reachable at your production root. It contains environment variables your staging environment uses — often a subset of production creds, but frequently reusable against staging surface or revealing internal service names.
Why it matters
Staging environments tend to share more with production than operators realise:
- Same OAuth client IDs and secrets registered with the provider (cheaper to manage one app than two).
- Same third-party API keys with looser rate limits and the same blast radius.
- Same database hostname patterns (
staging.db.internal.example.com) revealing internal naming. - Same encryption keys for cookies/JWTs — meaning a forged session in staging is valid in production.
Even when the credentials in .env.staging are throwaway, the file's variable NAMES reveal which third-party services you depend on (Datadog, Stripe, Twilio, SendGrid, Postmark, etc.), which feature flags exist, and which internal microservices the staging app talks to. That's a reconnaissance map.
How it ends up deployed: build tool copies .env.* patterns indiscriminately, or a build script does cp .env.staging dist/ instead of cp .env.production dist/, or someone debugging production-staging-parity issues left the file in the deploy.
How to fix it
- Delete the file from your production deploy immediately. Push a build that excludes it.
- Rotate any credential in the file that is shared with production. Don't assume "but it's only staging" — assume it's already been harvested.
- Audit your build script. Production builds should ONLY include the env vars the production runtime reads.
cp .env*is too broad; be explicit:cp .env.production dist/.env. - Add a guard: post-build check that fails the deploy if any
.env.*file ends up in the public output directory. - At the edge, block all
.env*paths:_redirectsreturning 404, or a Pages Function returning 404 for any path matching/\.env.*.
Did vibecheck flag this on your app?
If you reached this page from a vibecheck inspection report, the redacted match in your scan output is the exact string we found in your bundle. After applying the fix above, run the inspection again — the finding should clear.
Run another inspection