Most leaked API keys are not stolen by clever attacks. They are committed to a Git repository by accident, copied into a screenshot, or left in a web-readable file. This guide shows how to keep database passwords, payment gateway keys and other secrets out of your code with environment variables and .env files, for PHP and Node.js, and where to put them on shared hosting, a VPS and the App Platform.
Keep every secret out of your code: read it from an environment variable, keep local values in a .env file that is listed in .gitignore, and commit only a .env.example with empty values. On the server, keep the real .env outside the web root with permissions 600, or use the platform's own environment variable settings. If a secret is ever committed or exposed, revoke it at the provider first, then deploy a new one.
1. Why configuration belongs in the environment
The idea comes from the Twelve-Factor App: anything that changes between your laptop, staging and production, such as database details, API keys and URLs, lives in the environment, not in the code. The same code then runs everywhere, and a copy of the repository reveals nothing.
Automated scanners search public repositories constantly for strings that look like cloud, payment and email API keys. A key pushed to a public repository can be found and abused within minutes, and deleting the commit later does not undo the exposure.
2. The .env file pattern
A .env file is plain text with one KEY=value per line:
APP_ENV=production
APP_URL=https://yourbusiness.in
DB_HOST=localhost
DB_NAME=shop_prod
DB_USER=shop_user
DB_PASSWORD=change-me
RAZORPAY_KEY_SECRET=change-meTwo rules make it safe:
- Never commit the real file. Add these lines to
.gitignorebefore your first commit:
.env
.env.*
!.env.example- Commit a
.env.exampleinstead, with the same keys and empty or dummy values, so a new developer knows what to set.
Check that Git really ignores it with git check-ignore -v .env. If it prints nothing, the file is not ignored.
3. Reading variables in PHP
The common library is vlucas/phpdotenv:
composer require vlucas/phpdotenv<?php
require __DIR__ . '/vendor/autoload.php';
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
$dotenv->required(['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD']);
$dbPassword = $_ENV['DB_PASSWORD'];required() stops the app with a clear error if a variable is missing, instead of failing later in a confusing way. Read values from $_ENV; the default loader does not fill getenv().
Laravel loads .env for you. Call env() only inside the files in config/, and read values elsewhere with config(). After php artisan config:cache, env() calls outside config/ return null.
4. Reading variables in Node.js
Node.js 20.6 and later can load a .env file itself:
node --env-file=.env server.jsOn any version, the dotenv package does the same:
import 'dotenv/config'; // or: require('dotenv').config();
const required = ['DB_PASSWORD', 'RAZORPAY_KEY_SECRET'];
const missing = required.filter((k) => !process.env[k]);
if (missing.length) {
console.error(`Missing environment variables: ${missing.join(', ')}`);
process.exit(1);
}Frameworks such as Next.js read .env files on their own. In Next.js, only variables prefixed NEXT_PUBLIC_ reach the browser, so never give a secret that prefix. The same applies to any front-end build: a value bundled into browser JavaScript is public.
5. Where production secrets live
| Where your app runs | Put secrets in | Notes |
|---|---|---|
| Shared hosting, PHP site | A .env file one level above public_html | Permissions 600; see the note below on Composer |
| Shared hosting, Node.js or Python app | The app's Environment variables section in the panel | Save, then restart the app |
| Your own VPS, systemd service | An EnvironmentFile= owned by root, mode 600 | systemctl daemon-reload and restart after changes |
| Your own VPS, Docker | --env-file or env_file: in Compose | Never bake secrets into the image |
| Domain India App Platform | The app's Env Vars tab | Values are encrypted at rest and masked |
Keep .env out of the web root. If a .env sits inside public_html, anyone may be able to download it by typing its address. Put it one level up and point your loader there, for example Dotenv::createImmutable(dirname(__DIR__)). If your framework must live inside public_html, block the file in .htaccess:
<Files ".env">
Require all denied
</Files>Then set permissions to 600 in File Manager or with chmod 600 .env, and open https://yourbusiness.in/.env in a browser to confirm it is refused.
On a VPS with systemd:
# /etc/systemd/system/myapp.service
[Service]
EnvironmentFile=/etc/myapp/myapp.env
ExecStart=/usr/bin/node /srv/myapp/server.js6. Secrets in CI/CD
Never write a secret into a workflow file. In GitHub, add it under Settings › Secrets and variables › Actions and reference it by name:
- name: Deploy
env:
SSH_KEY: ${{ secrets.DEPLOY_SSH_KEY }}
run: ./deploy.shGitHub masks secret values in logs, but a script that prints or transforms them can still leak them, so don't echo secrets in build steps. For a full pipeline to a server you manage, see GitHub Actions CI/CD to a VPS.
7. What is a secret, and what is just config
| Kind of value | Where it belongs | Example |
|---|---|---|
| Passwords, API keys, tokens, signing keys | Environment variable or secret store | DB_PASSWORD, RAZORPAY_KEY_SECRET |
| Per-environment settings | Environment variable | APP_URL, APP_ENV |
| Feature switches | Environment variable or a config file | FEATURE_NEW_CHECKOUT=true |
| Non-sensitive constants | A committed config file | page size, default language |
A useful test: if you would be uncomfortable seeing the value in a public screenshot, it is a secret.
8. Rotating secrets and handling a leak
Rotate a secret when someone who knew it leaves, when a device or laptop is lost, and whenever you suspect exposure. Rotate without downtime in this order: create the new value at the provider, deploy it, confirm the app works, and only then revoke the old one.
1. Revoke or rotate the secret at the provider immediately, before anything else. The damage is the live key, not the Git history.
2. Check the provider's logs for use you don't recognise since the commit.
3. Deploy the new value.
4. Clean the history with git filter-repo if you want, but assume anyone who cloned or saw the repository already has the old value.
To catch mistakes early, run a scanner such as gitleaks as a pre-commit hook or in CI, and use trufflehog to audit a repository's full history. On GitHub, secret scanning and push protection block many known key formats before they are pushed.
9. Running this on Domain India
- Shared hosting (cPanel, DirectAdmin): Composer cannot run on our shared servers, so run
composer installon your own computer or in CI and upload the project with itsvendor/folder. Keep.envabovepublic_html. For Node.js and Python apps, use the panel's app page: see deploying a Node.js app and deploying a Python app. Jailed SSH is available on every shared plan; it is off by default, so ask support to enable it. - App Platform: set secrets in the app's Env Vars tab; see App Platform environment variables.
- VPS: you manage the server, so use systemd
EnvironmentFile, Docker env files or a secret manager as above.
- 512 MB RAM per app
- 1 vCPU
- 5 GB NVMe SSD
- PostgreSQL Database
Prices on the cards exclude 18% GST.
Frequently asked questions
Is a .env file encrypted?
No. A .env file is plain text. It is protected only by keeping it out of Git, out of the web root and readable by your own user alone, with permissions 600. For encryption at rest, use a platform's secret settings or a dedicated secret manager.
Should I commit .env.example?
Yes. Commit .env.example with the same keys and empty or dummy values, so others know what to set. Never commit .env, .env.production or any file with real values.
Where should I put the .env file on shared hosting?
One level above public_html, so it can't be downloaded over the web, with permissions 600. If it must be inside public_html, deny access to it in .htaccess and test that https://yourdomain/.env is refused.
Can I use Composer to install phpdotenv on Domain India shared hosting?
Not on the server. Composer cannot run on Domain India shared hosting, so run composer install on your own computer or in CI and upload the project together with its vendor folder.
How do I set environment variables for a Node.js app on cPanel?
Open your app in the panel's Node.js app page and add them in the Environment variables section, then save and restart the app. Read them in your code with process.env.
I pushed an API key to GitHub. What do I do first?
Revoke or rotate the key at the provider immediately, then check the provider's logs for misuse and deploy the new key. Rewriting Git history is optional and does not help if anyone already copied the repository.
Is it safe to log environment variables while debugging?
Not all of them. Never print the whole environment, because logs are often kept and read by more people and tools than you expect. Log only the specific non-sensitive values you need.
Ready to deploy with your secrets kept safe? Look at the App Platform for apps with built-in environment variables, cPanel hosting for PHP sites, or a VPS for full control.
Set your app's secrets in the dashboard, encrypted at rest and masked on screen, and deploy without putting keys in your code.
See the App Platform