Fundamentals · 7 min read
What is a .env file?
Last updated
The short answer
A .env file is a plain-text file that stores configuration for an application as KEY=value pairs. When the application starts, a loader reads the file and exposes each pair as an environment variable. Code then reads settings such as a database host or an API key from the environment instead of hard-coding them.
The idea comes from the Twelve-Factor App methodology, which recommends keeping configuration that changes between deployments in the environment, separate from the code. The .env file is a convenient way to provide those variables during development and on simple servers. Loader libraries named "dotenv" exist for Ruby, Node.js, PHP, Python, Go and many other languages.
The format
There is no single formal specification, but most loaders agree on the basics:
# Comments start with a hash
APP_ENV=production
APP_URL=https://example.com
# Values with spaces are quoted
MAIL_FROM_NAME="Example Shop"
# Some loaders accept an "export" prefix, so the file can also be sourced by a shell
export DB_HOST=db.internal
DB_PORT=5432
DB_PASSWORD=change-me- Keys are usually upper-case with underscores. Names are case-sensitive.
- Everything after the first
=is the value. Quoting rules, escape sequences and multi-line values differ between loaders. - Some loaders support variable expansion such as
DATABASE_URL=postgres://${DB_HOST}/app. Others treat it as a literal string. - Most loaders do not overwrite variables that are already set in the real environment, so a value configured on the server takes precedence over the file.
.env, .env.local, .env.production and friends
Many frameworks load several files in a defined order so that you can combine shared defaults with machine-specific or environment-specific overrides:
| File | Typical purpose |
|---|---|
.env | Base configuration or defaults. In some frameworks (for example Symfony) it is committed with safe defaults. |
.env.local | Local overrides and real secrets for one machine. Never committed. |
.env.production, .env.prod | Values for the production environment, often loaded when the app runs in production mode. |
.env.example | A template listing required keys without real values. Safe to commit. |
That is why IsMyENVPublic checks /.env, /.env.local, /.env.production and /.env.prod: they are the names most likely to hold real production values.
Which tools read .env files
- Laravel loads
.envfrom the project root through the phpdotenv library. - Symfony loads
.env,.env.local,.env.$APP_ENVand.env.$APP_ENV.local. - Node.js projects use the
dotenvpackage, and Node.js itself can load a file withnode --env-file=.env app.jssince version 20.6. - Next.js and Vite load
.env,.env.localand mode-specific files such as.env.production. - Django, Flask and Rails projects commonly use
python-dotenv,django-environordotenv-rails. - Docker Compose reads a
.envfile next to the compose file for variable substitution.
Why the file is sensitive
A production .env usually collects the most valuable secrets of an application in one place:
- database hosts, user names and passwords,
- application encryption and signing keys, such as Laravel's
APP_KEYor a JWT secret, - cloud credentials such as
AWS_ACCESS_KEY_IDandAWS_SECRET_ACCESS_KEY, - API keys for payment providers, mail services, error tracking and AI services,
- internal hostnames that reveal how the infrastructure is built.
With these values, an attacker may be able to read or change the database, send email in your name, forge session cookies, or run up costs on your cloud account, without exploiting any vulnerability in the code.
Scanners look for it constantly
/.env are among the most common automated probes on the internet. If you look at the access log of almost any public web server, you will find them. An exposed file is usually found within hours or days, not months.How .env files become public
The file is harmless as long as the web server never serves it. Exposure almost always comes from one of these mistakes:
- Wrong document root. The web server publishes the project root instead of its
public/directory, so every file in the project is downloadable. This is common on shared hosting when an application is uploaded intopublic_html. - Missing deny rule. The server serves static files by default, and nothing tells it that dotfiles are off-limits.
- Static file middleware on the wrong folder. A Node.js server calls
express.staticon the project directory. - Deployment artifacts. The file is copied into a static export, a Docker image that serves files, or a publicly readable storage bucket.
Git is a related risk: a committed .env exposes secrets to anyone with access to the repository, and a publicly served .git/ directory can leak it as well.
Good practice
- Commit a
.env.examplewith key names only, and add.envand.env.*.localto.gitignore. - Keep the file outside the directory your web server publishes, and readable only by the application user.
- Add a web server rule that denies dotfiles as a second line of defense. See the Nginx and Apache guides.
- On platforms that support it, prefer real environment variables or a secrets manager over a file on disk in production.
- Use separate credentials per environment, so a leaked development file doesn't unlock production.
Remediation checklist
.envis listed in.gitignoreand has never been committed- The web server's document root is a dedicated public folder, not the project root
- A web server rule denies access to dotfiles (except
/.well-known/) - File permissions restrict the file to the application user (for example
chmod 600or640) - Production uses different credentials from development
- The IsMyENVPublic check reports no exposure after each deployment change