A .env file is where an application keeps the values that change between environments: database URLs, API keys, webhook signing secrets, OAuth client secrets. That's convenient for running code, and it makes the file one of the most sensitive things a developer handles. When a teammate needs it, the file usually travels by chat or email, and every copy left behind is a credential someone has to remember to clean up.
This guide covers what to do before you send a .env file, how to send it, and what to do afterwards.
Why .env files need more care than other config
The Twelve-Factor App popularised keeping config in environment variables, and it offers a useful test: could the codebase be made open source at any moment without compromising any credentials? The .env file is where those credentials end up, which is why the dotenv README answers "Should I commit my .env file?" with a plain "No."
The same reasoning applies to sending it. A .env pasted into team chat is visible to everyone in the channel, stays in the history, and can turn up in exports: on Slack, workspace owners and admins can export messages from public channels, and on some plans from private channels and DMs too. An email attachment sits in both mailboxes and their backups. Stripe's API key documentation is direct about it: don't share keys over email, chat or other unencrypted channels.
Before you send: shrink the file
The safest secret is the one you never send. Go through the file line by line first.
- Remove what they don't need. A frontend developer rarely needs the payment provider's secret key or the production database URL. Send the three variables they asked for, not the forty in your file.
- Prefer development and sandbox values. Many providers issue separate test credentials. Stripe's sandbox keys (
sk_test_,rk_test_) can't move real money, and someone building locally rarely needs live keys. - Give people their own keys where the provider allows it. Google Cloud's API key guidance is to isolate keys and give each team member their own key for each application. A personal key can be revoked without breaking anyone else.
- Keep one file per environment. dotenv recommends one
.envfile per environment rather than files that inherit from each other, which also makes it obvious which file you're about to send. - Commit a
.env.examplewith the variable names and no values, so the next person knows what exists without anyone sending a file.
Keep it out of git while you're at it
Plenty of .env leaks happen through commits rather than messages. A few lines in .gitignore close that door:
.env
.env.*
!.env.example
The ! prefix re-includes a file that an earlier pattern excluded, which keeps the example file tracked (gitignore documentation). .gitignore doesn't affect files Git already tracks, so if .env was committed before, stop tracking it with git rm --cached .env.
Then add secret detection before code leaves your machine. The OWASP Secrets Management Cheat Sheet recommends enabling secrets detection at the developer level, for example in a pre-commit hook. On GitHub, push protection blocks pushes that contain recognised secrets before they reach the repository.
If a .env file did reach a repository, rotate the secrets first. GitHub's guide to removing sensitive data makes revoking or rotating the secret the first step, and notes that rewriting history can't remove copies already in other people's clones.
How to send a .env file with an expiring encrypted link
When someone genuinely needs the values, send them through a link that's encrypted in your browser and deletes itself. With ProtectedShare's EnvShare:
- Paste the trimmed file. It's encrypted in your browser with AES-256-GCM before anything is uploaded.
- Set the number of reads. One read if one person will open it once; two or three if they'll open it on a laptop and then on a server. The limit can be anything from 1 to 100, so pick the smallest that works.
- Set a short expiry. One hour if they're waiting for it, one day if they aren't. The longest allowed is 30 days.
- Optionally require a password, and send that password through a different channel, such as a phone call. A leaked link alone then opens nothing.
- Send the link in your usual chat. The decryption key sits after the
#in the link, and browsers don't send that part to the server (MDN). - Check it was opened. My links, in the browser that created the share, shows whether the link was opened or has expired, and lets you revoke one that hasn't been opened yet.
After the last allowed read, or at expiry, the encrypted row is deleted. What stays in the chat is a link that no longer opens anything.
If you also need to send certificates or other binary files, use an encrypted file drop instead (up to 25 MB, with a download limit). If you're the one who needs a .env from someone else, send them a secret request: whatever they paste is encrypted in their browser to a key only your inbox link holds.
Common mistakes
- Sending the whole production file when the person needed one value.
- Pasting it into a ticket, wiki page or pull request comment. Those are built to be searched and kept.
- Splitting the file across two messages in the same chat. Anyone with access to the chat has both halves.
- Taking a screenshot. Screenshots end up in photo libraries and their cloud backups.
- Leaving it in the recipient's Downloads folder. Ask them to move the values into their local config or password manager and delete the file.
Which method fits which situation
| Situation | What to do |
|---|---|
| New developer setting up locally | Personal dev or sandbox keys, and a one-read link for anything shared |
| Teammate needs one production value during an incident | One-read link with a one-hour expiry; rotate afterwards if it was a shared key |
| Contractor joining for a few weeks | Their own scoped keys where possible, a link for the rest, rotation at the end |
| CI/CD or a server needs the values | The platform's secret store, never a link |
Someone needs to send you their .env | A secret request |
When not to use a link at all
A one-time link is for the exceptions. If your team passes .env files around every week, the process is the problem. Move runtime secrets into the secrets manager your platform provides, or keep them encrypted in the repository with tooling built for it; OWASP notes that secrets can be stored encrypted in git, with decryption limited to the consumer that needs them. Onboarding then means granting access, not sending files. Secrets management basics for startups describes a lightweight way to get there.
After sharing: rotate on triggers
You don't need to rotate every value each time a file is shared, but some events should trigger it:
- someone with access leaves the project or changes role, which Stripe lists as a reason to rotate keys;
- the link shows as opened before the recipient says they opened it;
- the file, or any value from it, was pasted somewhere public or committed;
- a contractor engagement ends.
When you rotate, create the new value, deploy it, confirm the old one is no longer in use, then revoke it. Some providers make this smoother with an overlap period: Stripe keeps a rotated key working for up to 7 days. For new random values such as webhook or session secrets, use a secret key generator.
Sources
- The Twelve-Factor App, Config
- dotenv, README
- Git, gitignore documentation
- OWASP, Secrets Management Cheat Sheet
- GitHub Docs, Push protection and Removing sensitive data from a repository
- Stripe Docs, API keys
- Google Cloud, Best practices for managing API keys
- Slack Help Center, Export your workspace data
- MDN, URI fragment