To find environment variables missing from a project, compare your .env with .env.example by key name, not line by line. FindUtils Env Diff parses both files with the dotenv rules and lists keys only in one file and keys whose values differ, in your browser. The files are not uploaded, and secret-looking values are masked in the result by default.

This guide covers why the two files drift apart, which file to put on which side, how to read the result, and what the comparison deliberately does not do.

Why .env and .env.example Drift Apart

The .env file holds real values and is kept out of the repository. The dotenv project's own answer to "should I commit my .env file?" is "No", and the Next.js documentation warns that you almost never want to commit these files. That leaves .env.example (or .env.template, .env.dist) as the only shared record of which keys exist.

Drift happens because only one of the two files is used every day:

  • A developer adds a key locally. The code reads it, their .env has it, and nobody updates the example.
  • A key is renamed. MAIL_HOST becomes SMTP_HOST in code and in one person's .env; the example keeps the old name.
  • A key is retired. The example still lists it, so new setups carry a value nothing reads.

None of these produce an error until someone starts from the example and the app fails at runtime.

Which File Goes on Which Side?

Env Diff reads the comparison as a change from File A to File B. Keys only in B are added, keys only in A are removed, and keys in both with different values are changed.

Question you are askingFile AFile BWhat to act on
What is missing from the example?.env.example.envAdded keys: add them to the example with a placeholder
What does my setup lack?.env.example.envRemoved keys: add them to your .env
What differs between two servers?staging fileproduction fileChanged keys, then added and removed
What did a branch change?old .env.examplenew .env.exampleEverything listed

The labels on each pane are editable, so the report can say "staging" and "production" instead of A and B.

How to Compare the Two Files

Step 1: Paste or drop both files

Open Env Diff, paste .env.example into File A and .env into File B, or drop the files on the panes.

Step 2: Choose what the result shows

Values in the result decides what is visible: mask secret-looking values (the default), mask all values, or show all values. Ignore key case treats API_KEY and api_key as one key. Show unchanged keys adds the keys that match to the list.

Step 3: Compare and read the groups

Press Compare. The summary counts added, removed, changed and unchanged keys, and each group lists its keys. A changed key shows the old value and the new one.

Step 4: Keep the report

Copy the report or download it as env-diff-report.txt or env-diff-report.json. Both downloads use the same masking as the screen, so a masked report is safe to paste into a ticket.

What Counts as the Same Value?

Env Diff compares values after the dotenv rules are applied, the same way a dotenv loader would read them. These pairs compare as equal or different:

File A lineFile B lineResult
PORT=3000export PORT=3000Same: the export prefix is dropped
NAME=appNAME="app"Same: quotes are removed before comparing
NAME=app # mainNAME=appSame: # after a space starts a comment on an unquoted value
NAME=app#mainNAME=appChanged: a # with no space before it is part of the value
MSG="a\nb"MSG='a\nb'Changed: double quotes turn \n into a line break, single quotes keep it as two characters
URL=$HOST/apiURL=db.example.com/apiChanged: $HOST is compared as text, never expanded

The order of lines never matters, because keys are matched by name. Comments and blank lines are ignored. A key written twice in one file keeps its last value, and a warning names both line numbers.

How Masking Works, and Where It Stops

With the default setting, a value is hidden when its key name contains SECRET, TOKEN, PASSWORD, PASSWD, PWD, KEY, AUTH, PRIVATE, CREDENTIAL, SALT, DSN, COOKIE or SESSION, in any letter case. A masked value shows up to eight dots and its length, such as •••••••• (40 chars), which is often enough to spot an empty or truncated secret.

The default also hides the password inside a connection URL under any key name: DATABASE_URL=postgres://app:hunter2@db:5432/app is shown as postgres://app:••••@db:5432/app, with the rest of the address readable. Beyond that, masking goes by the key name, and that cuts both ways:

  • Other secrets under plain names are shown. An API token stored under PAYMENTS or a signed address in WEBHOOK_URL=https://hooks.example.com/abc123 is visible, because neither name contains one of the words and neither value has a user:password@ part. Choose Mask all values before taking a screenshot of such a file.
  • Harmless keys can be hidden. SESSION_LIFETIME=120 and PUBLIC_KEY_PATH=./keys/pub.pem are masked because their names contain SESSION and KEY. Choose Show all values when you need to read them.

Masked values are still compared on their real text, so a changed secret is reported as changed even when both sides show dots.

Comparing Staging and Production

The same comparison works for any two dotenv files, not just a template and a copy. Save each environment's variables as a KEY=value file, put the one you trust in File A, and read the changed group first: a production value that differs from staging on a key like APP_URL or QUEUE_CONNECTION is usually on purpose, while one on a key like CACHE_PREFIX might not be.

Keep in mind that a framework may read more than one file. Next.js, for example, looks for a variable in process.env, then .env.$(NODE_ENV).local, .env.local, .env.$(NODE_ENV) and .env, stopping at the first one that defines it. Two .env files that match can still produce different settings if a .env.local or a variable set by the host sits above them. To compare the effective set, combine the layers first with Env Merge and compare the merged files.

What the Comparison Does Not Do

  • It does not expand variables. Next.js expands $VARIABLE references inside .env files; Env Diff compares the text as written.
  • It does not check that values work. A wrong database password is compared like any other string. Nothing is fetched and no service is called.
  • It does not guess at broken lines. A line with no =, an invalid key name or a quote that never closes stops the comparison, and the message names the file and the line number, so no key is invented.

For the problems inside one file, such as duplicate keys, unquoted values with spaces and invalid names, run it through Env Linter first.

Common Mistakes

Swapping the sides. With .env in File A and the example in File B, the missing keys appear as removed instead of added. The keys are the same; only the labels flip. Set the pane labels so the report reads correctly.

Pasting unmasked output into a chat. The report you copy follows the masking setting. Check the setting before copying a comparison of real secrets.

Treating "no differences" as "no problems". Two files can have the same keys and both be wrong. The comparison answers which keys and values differ, nothing more.

Tools Used in This Guide

ToolUse
Env DiffCompare two .env files by key: added, removed, changed
Env MergeCombine a base .env with an overlay before comparing layered setups
Env LinterCheck one .env for duplicate keys, invalid names and quoting problems
Env JSON ConverterTurn a .env into a flat JSON object, or JSON back into KEY=value lines
JSON DiffCompare two JSON config files by value

FAQ

How do I check that .env.example is up to date?

Compare .env.example (File A) with a working .env (File B) in Env Diff. Every key under Added is used in your .env but missing from the example. Add those keys to the example with a placeholder value, and remove any keys under Removed that the code no longer reads.

Can I compare .env files that contain real secrets safely?

The files are read in your browser tab and are not uploaded. Secret-looking values and passwords inside connection URLs are masked in the result by default, and Mask all values hides every value before you screenshot or copy the report.

Why does a key show as changed when the values look the same?

The parsed values differ. Common causes are a # without a space before it, which stays part of an unquoted value, single quotes that keep \n as two characters where double quotes make a line break, or trailing spaces inside quotes.

Does Env Diff work with .env.local, .env.production and other names?

Yes. Any file with KEY=value lines in dotenv syntax works, whatever its name. Paste or drop it into either pane.

Next Steps

Compare your .env against the example with Env Diff. When you need one file from several layers, combine them with Env Merge first.