Server
Managing secrets and keys: .env, tokens and rotation
Where to keep .env, API tokens, database passwords and encryption keys, who should see them and when to replace them — a practical guide.
· 8 min read

Every web and mobile app works with secrets: the database password, APP_KEY, SMS or payment provider tokens, the SMTP password, third-party API keys. However carefully the code is written, a single leaked token can be enough to get in. So before the expertise, check secrets not by "they're not in the code" but by asking: where do they live, who can see them, when were they last changed?
Where secrets live
Start with an inventory. Secrets usually turn up in:
- the
.envfile on the server; - the repository and its history (old commits too);
- CI/CD settings and build logs;
- backups (the archive includes
.env); - application logs (along with request bodies or headers);
- the mobile app build (strings inside the APK/IPA);
- team chats and documents.
Make a simple table for each secret: name, what system it unlocks, who has it, when it was last changed.
1. Keep .env out of the repository
Check that .env is in .gitignore — but that is not enough, because the file may have been committed earlier. Scan the history:
gitleaks detect --source . --log-opts="--all"
If a real secret turns up in history, removing it from history is not sufficient: copies of the repository may already exist elsewhere. The correct fix is to replace the secret (rotation). .env.example should contain only key names with empty or sample values.
2. Make sure .env cannot be opened in a browser
In Laravel and other frameworks the web server root must be the public/ directory. If the root is misconfigured, https://site.uz/.env downloads directly. Extra protection in Nginx:
location ~ /\.(?!well-known) {
deny all;
}
To check: curl -I https://site.uz/.env and curl -I https://site.uz/.git/config should both return 403 or 404.
3. File permissions
Only the user the app runs as and the deploy user should be able to read .env:
chown deploy:www-data .env
chmod 640 .env
If one server hosts several sites, each should run under its own user. Otherwise a weakness in one site exposes the neighbour's secrets too.
4. Least privilege
Each token should be able to do only what it needs:
- the app connects to the database not as
rootor a superuser but as a user with rights to its own database only; - third-party API keys are restricted by IP or domain where possible;
- write access is not granted where read access is enough;
- test and production use separate keys.
That way a single leaked key causes limited damage.
5. No secrets in logs
Error logs often record the full request body: password, OTP code, the Authorization header. Tune the log level for production, mask sensitive fields and verify that storage/logs is not reachable from a browser. APP_DEBUG=false in Laravel belongs here too: the debug page can display environment variables.
6. Do not store secrets in the mobile app
Unpacking an APK or IPA and reading its strings is not hard. So do not embed server-side secrets (a payment provider's secret key, an admin token) in the app — they belong on the backend only. After the mobile expertise of our own product we stopped storing endpoints as plain strings, enabled R8 minification and Dart --obfuscate, and moved user tokens into Keystore/Keychain via flutter_secure_storage. Obfuscation makes analysis harder but does not protect a secret — the secret should not be in the app at all.
7. Rotation: when and how
Rotation means replacing a secret with a new one and revoking the old one. Always do it when:
- the secret appeared in a repository, log or chat;
- someone with access leaves the team;
- something suspicious happens on the server or an employee's computer;
- before the expertise, if secrets have not been changed for a long time.
A safe rotation sequence:
- Create the new key (in the provider's panel, or a new password).
- Put the new value in
.envand reload the app (php artisan config:cache, restart PHP-FPM and queue workers). - Confirm the app works.
- Revoke the old key on the provider's side.
- Update the date in your table.
Deleting a secret does not make it safe. A leaked secret is neutralised only by replacing it.
A note on APP_KEY
In Laravel, APP_KEY is used for cookies, sessions and data encrypted with Crypt. Replace it outright and active sessions and encrypted fields become unreadable. Since Laravel 11 there is an APP_PREVIOUS_KEYS variable: put the old key there, and the app can still read old data while encrypting new data with the new key.
8. Backups
Backup archives often contain the whole project directory, .env included. Keep them outside the web root, encrypt them and restrict who can access them. Check that files such as backup.zip or site.tar.gz have not been left in public/.
Short checklist
.envis not in the repository or its history./.envand/.git/configcannot be opened from a browser..envpermissions are640with the correct owner.- The app connects to the database as a restricted user.
- Logs contain no passwords, tokens or OTPs.
- The mobile build contains no server secrets.
- Every secret has a known owner and last rotation date.
Summary
Secret management starts not with sophisticated tools but with order: inventory, least privilege, closing exposed spots and regular rotation. These steps bring peace of mind at the expertise and in day-to-day operation.
Before the expertise, Sipar also checks server settings, the repository and builds for secrets, and closes whatever it finds. Request a security scan.


