ALL POSTS

Exposed .env File? Check and Protect Your Config

October 26, 2026 · 6 min read

website-securitysecrets
A glowing document outside an open vault in a dark server room.

An exposed .env file can turn a deployment mistake into a credential incident. The file may contain database passwords, signing keys, or privileged service tokens that were intended to remain on the server. The first job is to establish whether your own website actually serves the file. The next is containment and credential rotation, not merely hiding the URL.

This guide also covers Git metadata, backups, temporary configuration copies, and diagnostic pages. Keep the checks limited to sites you own or have permission to assess. You do not need to download an entire repository or database to establish that an unwanted file is public.

How sensitive files reach a website

A frequent deployment pattern is copying a working directory into the web server’s document root. That directory may contain source control metadata, local environment files, a release archive, and files an editor created while saving. A web server has no automatic understanding of which files your team considers private.

Other mistakes are less obvious. A build script may copy too much into public/. A backup job may write a SQL dump beside static assets. An emergency phpinfo page may survive after troubleshooting. A storage bucket used for private artifacts may later receive a broad public policy.

The durable fix is a narrow deployable artifact and an explicit public directory. Blocking a few filenames is a useful extra layer, but it cannot anticipate every backup name someone might invent.

Check if .env is public without spreading its contents

First inspect your deployment artifact and hosting configuration. Confirm the document root and check whether secrets or repository metadata exist beneath it. Then, if needed, make one targeted request to a known path on your own site. Store the response only in a restricted temporary directory.

# Replace example.com with a host you own.
umask 077
check_dir="$(mktemp -d)"
curl --max-time 10 --max-filesize 102400 -sS \
  -D "$check_dir/headers.txt" \
  -o "$check_dir/body.txt" \
  https://example.com/.env
printf 'Review locally: %s\n' "$check_dir"

Do not paste the response into an issue tracker, chat, or public scanner result. Review enough to distinguish actual configuration from an error page, redact values in any evidence, then delete the temporary files through your normal secure handling process. A failed or truncated request is not proof that the file is absent.

A 200 response may be a soft 404

Single-page applications often return their HTML shell for unknown routes. A request for /.env might therefore receive HTTP 200 and the same page as /anything-that-does-not-exist. Status-only detection would label that as a serious exposure even though no environment file was returned.

This was a real implementation problem in Scanwise: exposure checks needed a missing-path baseline and content validation, rather than a successful status alone. Compare a harmless, randomly named nonexistent route with the suspected file. If both responses are the same application shell, investigate routing before calling it a leak.

Real .env evidence usually resembles configuration assignments; real Git HEAD metadata has its own recognizable format. These signatures improve confidence but are not infallible. An authentication gateway can return a different page on each request, and a custom error page may contain example configuration. Keep the conclusion tied to the response you observed.

What else should you inspect?

Review only the locations that make sense for your deployment:

  • Git metadata beneath the served root, including a reachable .git/HEAD.
  • Backup archives and database dumps produced by your own jobs.
  • Editor copies such as config.php.bak or files left during a migration.
  • Temporary diagnostic endpoints, including phpinfo pages.
  • Old releases reachable through an unintended directory or hostname.

A .git folder exposed to the web is a deployment boundary problem even when one file looks harmless. Remove the metadata from the artifact and deny the path. Do not demonstrate the risk by reconstructing credentials or downloading unrelated content.

Nginx: deny known private paths before SPA fallback

The following example blocks dotfiles except the well-known directory used by legitimate protocols. It also blocks common backup extensions. Adapt it to the site; a business that intentionally publishes ZIP downloads needs a more targeted rule.

# Add within the appropriate server block.
location ~ /\.(?!well-known(?:/|$)) {
    return 404;
}

location ~* \.(?:sql|bak|old|orig|save|zip|tar|tgz)$ {
    return 404;
}

location / {
    try_files $uri $uri/ /index.html;
}

Inspect existing exact and ^~ locations because they can alter which rule wins. Test syntax, reload carefully, and repeat the targeted requests. Keep ACME and security.txt available under /.well-known/. The Nginx location documentation explains request matching and precedence.

Next.js, Express, and deployment artifacts

For Next.js, files placed in public/ are intended to be served. Keep .env files and backups outside that tree, and inspect anything copied there during a build. Also remember that browser-exposed environment variables are a separate leak path: hiding .env does not remove a value compiled into JavaScript.

For Express, mount a dedicated static directory rather than the repository root:

import path from 'node:path';
import express from 'express';

const app = express();
app.use(express.static(path.resolve('public'), {
  dotfiles: 'deny',
  index: false,
}));

The Express static middleware reference documents these options. This does not protect another route that deliberately reads and returns a private file. Review all file-serving code and ensure backups are never written into the static directory.

If a secret really was exposed

Contain the route or artifact immediately. Preserve minimal, access-controlled evidence: affected path, timestamps, deployment version, and relevant access logs. Treat privileged credentials in the file as potentially compromised even if logs do not prove somebody downloaded them.

Rotate or revoke credentials at their issuing service. Update dependent deployments and confirm the old credential no longer works. For signing keys, decide whether existing sessions or tokens must be invalidated. Purge cached copies where applicable, while recognizing that caches and Git history cannot make a previously disclosed secret private again.

Avoid rotating everything blindly in a single untested operation. Inventory each consumer, create replacements where the provider supports overlap, deploy them, verify normal behavior, then revoke the old credential. Escalate immediately when containment cannot wait for that sequence.

Make the fix survive the next release

Add an artifact inspection step and a small regression check for the specific path that was exposed. Document which directory is public, who owns backup storage, and how temporary diagnostic pages expire. Retest after changes to hosting, reverse proxies, and build scripts. Use the broader website security checklist to track related fixes without turning one incident into an unowned backlog.

Check your own site — your first scan is free. Scanwise uses passive intelligence and non-destructive external probes to flag potential exposure and generate AI-assisted fixes. It does not exploit findings or automatically change files. Verify evidence and proposed changes; the free starter credit requires a verified account and is subject to anti-abuse limits.

Check your own site

Run the same external recon on your own domain. First scan is free.

RUN AI SCAN — FIRST SCAN FREE