Why .env files break in ways that are hard to see
A dotenv file looks like the simplest format in the world: KEY=value, one per line. The trouble is that there is no official specification. Every loader — dotenv in Node, python-dotenv, Docker Compose, Vite, the Railway and Vercel dashboards — implements slightly different rules for quoting, comments and whitespace.
The failure mode is nasty because it is silent. Your app does not crash on a malformed line; it starts up with a variable containing production # the app environment instead of production, and you find out three hours later when a conditional takes the wrong branch.
The four bugs that cause most incidents
Inline comments on unquoted values. NODE_ENV=production # the environment is read as the literal string production # the environment by some parsers and as production by others. Quote the value or put the comment on its own line.
Values with spaces left unquoted. APP_NAME=My App is ambiguous. Docker Compose and several shells will stop at the first space. Write APP_NAME="My App".
Duplicate keys. Defining DATABASE_URL twice is not an error for most loaders — the last one silently wins. That is exactly how a staging URL ends up in production.
Spaces around the equals sign. PORT = 3000 can produce a key named PORT (with a trailing space) that your code will never find, because it is looking for PORT.
Secrets in .env files
A .env file is a convenient place to keep credentials and a terrible place to lose track of them. This validator flags values shaped like known credentials — OpenAI and Anthropic keys, GitHub tokens, AWS access key IDs, Slack tokens, PEM private keys and JWTs — as a reminder, not an accusation.
The rule that actually protects you is upstream: .env belongs in .gitignore, and .env.example with placeholder values belongs in the repository. If a real key has ever been committed, rotate it. Deleting the commit does not help — it stays in the history and in every clone.
What this tool does not do
It checks syntax and shape, not meaning. It cannot tell you that DATABASE_URL points at the wrong host, that a key has been revoked, or that your application needs a variable you forgot to define — it has no knowledge of your app.
It also does not enforce one parser's rules over another. Where loaders genuinely disagree, the report says so and suggests the form that works everywhere, which is usually "quote the value".