Automation & DevOps

.env files and environment variables: keeping secrets out of your codebase

Committing an API key or database password directly into your code is one of the most common, most avoidable security mistakes in software development, and it happens to experienced developers too, usually by accident rather than ignorance.

V Vinod Kulkarni
24 September 2026 · 7 min read
.env files and environment variables: keeping secrets out of your codebase

.env files and environment variables: keeping secrets out of your codebase

TL;DR: A .env file stores environment-specific configuration and secrets, database credentials, API keys, sensitive tokens, as simple key-value pairs outside of your actual application code, loaded into your application at runtime as environment variables rather than being hardcoded directly into files that get committed to version control. This separation exists specifically to prevent secrets from ending up in your git history (where they can be exposed publicly, particularly on a public repository, or remain a lingering risk even in a private one) and to let the same codebase run correctly across different environments, local development, staging, production, each with its own separate, appropriate configuration values without any code changes.

Why hardcoded secrets are a genuinely common, serious mistake

Directly writing a database password, an API key, or any other sensitive credential straight into a code file is a shortcut that works functionally, the application runs fine, which is exactly why it happens so often, even among experienced developers, usually not from ignorance but from the path of least resistance during rapid development, with the intention of "fixing it properly later" that does not always actually happen. The genuine risk: once a secret is committed to version control, it exists in your git history permanently unless deliberately and carefully removed (covered below), meaning even if you later delete the line from the current version of the file, the secret remains recoverable from the repository's history by anyone with access to it, and on a public repository, by literally anyone at all, a pattern responsible for a very large number of real, documented security incidents where credentials were discovered in public repositories, sometimes within minutes of being pushed, by automated scanning tools that actively search for exactly this pattern.

What a .env file actually is

A .env file is simply a plain text file containing key-value pairs, one per line, in a straightforward format (DATABASE_PASSWORD=your_actual_password, API_KEY=your_actual_key), which your application reads at startup or runtime and loads into its environment, making these values accessible to your code through standard environment variable access methods rather than being written directly into your application's source files. The file itself lives in your project's directory structure but is specifically and deliberately excluded from version control (covered next), meaning it exists locally on each specific environment (your development machine, your production server) with values appropriate to that specific environment, while your actual application code, which does get committed to version control, simply references the environment variable by name, containing no actual secret value directly within it at all.

Why .gitignore is the critical companion step

Creating a .env file provides no protection whatsoever on its own if it is not also explicitly excluded from version control via your project's .gitignore file, since without this exclusion, the .env file itself, containing your actual secrets, would simply be committed alongside your code, defeating the entire purpose of separating secrets from code in the first place. Adding a line for .env (and any environment-specific variants like .env.local or .env.production) to your .gitignore file ensures git never tracks or commits this specific file, a simple, one-line addition that is nonetheless the step most commonly and most consequentially forgotten, particularly by someone newer to this pattern who correctly creates the .env file itself but does not realize the additional, necessary .gitignore step is what actually provides the protection.

A useful complementary practice: committing a .env.example file (with the same variable names but placeholder, non-sensitive example values) to version control instead, giving other developers or your future self a clear template of what environment variables the application expects, without exposing any actual secret values.

How this applies specifically to WordPress

WordPress's own core configuration file, wp-config.php, contains database credentials and various security keys directly, and while wp-config.php itself is a necessary, standard part of a WordPress installation (not something you would typically move to a separate .env file in a standard WordPress setup, since WordPress core itself expects this specific file), the same underlying principle applies directly: wp-config.php should never be committed to a public or shared version control repository containing your actual, real database credentials, and should specifically be excluded via .gitignore if your WordPress project is version-controlled at all, similar to the .env pattern, with a wp-config-sample.php (which WordPress itself already provides by convention) serving the same template-without-real-secrets role that a .env.example file serves in other contexts. For a more custom WordPress project, particularly one with additional custom functionality making API calls to third-party services, using genuine environment variables (loaded via a small helper library, or defined directly in your server's environment configuration) for those additional API keys and secrets, separate from wp-config.php itself, follows the same principle for that additional, custom configuration.

Flowchart showing the correct pattern: application code references an environment variable by name, the actual secret value lives only in a .env file excluded from git via .gitignore, and a separate .env.example template with placeholder values is safely committed to version control

Environment-specific configuration across dev, staging, production

Beyond the security benefit, environment variables provide a genuinely practical mechanism for running the same codebase correctly across different environments without any code changes: your local development .env file points to a local database and uses test/sandbox API keys, your staging environment's separately maintained configuration points to a staging database and potentially sandbox payment gateway credentials (relevant to testing the payment gateways covered in our dedicated comparison safely), and your production environment's configuration, maintained separately and never checked into version control at all, contains your actual live database credentials and live, real API keys. This means the exact same application code can run correctly in each environment simply by having the appropriate, separately-maintained environment variables available at runtime, without needing any environment-specific code branches or manual code edits when moving between environments, directly complementary to the staging environment practices covered in our dedicated staging guide.

What to do if a secret has already been committed

If a secret has already been committed to version control, simply deleting the line in a subsequent commit does not remove it from history, meaning the secret remains present and recoverable in earlier commits indefinitely unless specifically, deliberately addressed. The correct, necessary response: first and immediately, rotate the exposed credential itself (change the password, regenerate the API key) at its actual source, since this is the only way to guarantee the previously exposed value is no longer valid regardless of who may have already seen it, and only after that, consider whether removing it from git history entirely (using specific git history-rewriting tools designed for this purpose) is also warranted, understanding that history rewriting is itself a more involved, disruptive operation, particularly on a shared repository with other collaborators, making credential rotation the genuinely essential, non-optional first step regardless of whether you also pursue history cleanup afterward.

A checklist

Step Why it matters
Create a .env file for secrets, separate from code Keeps secrets out of committed files
Add .env to .gitignore The actual protective step; without this, .env itself gets committed
Commit a .env.example template instead Documents expected variables without exposing real values
Never commit wp-config.php with real credentials Same principle applies to WordPress's own config file
If a secret is ever committed, rotate it immediately Deleting the line later does not remove it from git history

FAQs

Does using a .env file mean my secrets are completely secure?

It removes them from version control specifically, a major, important improvement, but the .env file itself still needs to be protected on whatever server or machine it resides on (appropriate file permissions, not exposing it via a misconfigured web server, covered in the context of similar file-exposure risks in our file permissions discussion), since security is layered and this addresses specifically the version-control exposure risk, not every possible way a secret could be exposed.

Can I use environment variables without a .env file at all?

Yes, environment variables can also be set directly at the operating system or hosting platform level (through your hosting control panel, a systemd service definition, or similar), without necessarily using a .env file as the specific mechanism; a .env file is simply a common, convenient way to manage and load a set of environment variables during local development and in many deployment contexts, not the only possible implementation of the underlying environment variable concept.

How do I know if I've accidentally committed a secret to my repository in the past?

Several dedicated secret-scanning tools can scan your repository's full history for patterns matching common credential formats (API key patterns, common password field structures), a worthwhile one-time audit for an existing project, particularly one that may have had secrets committed before this practice was properly established.

Does GitHub or other platforms automatically detect and alert on committed secrets?

Some platforms, including GitHub, do offer automated secret-scanning features that can detect certain common credential patterns and alert you, a useful additional safety net, though this should be treated as a backup detection mechanism rather than a substitute for properly excluding secrets from version control in the first place.

Is it safe to share a .env file directly with a team member instead of each person creating their own?

For genuinely shared, non-sensitive configuration, this can be reasonable, though for actual secrets specifically, each environment (including each developer's local setup, if using genuinely separate credentials) should generally maintain its own appropriately-scoped .env file rather than broadly sharing production-level secrets more widely than necessary, following the general security principle of limiting credential access to only what is genuinely needed.

Does this apply to serverless or cloud function deployments too?

Yes, the same underlying principle applies broadly across virtually any deployment context; serverless platforms and cloud function services generally provide their own environment variable configuration mechanism (set through the platform's own dashboard or deployment configuration) serving the same fundamental purpose as a .env file in a more traditional server deployment.

What's the difference between a .env file and a secrets manager service?

A .env file is a simple, file-based approach suitable for many projects, particularly smaller ones; a dedicated secrets manager service (a more sophisticated, centralized system for storing, rotating, and controlling access to secrets across potentially many applications and environments) offers additional capability, more sophisticated access control, audit logging, automated rotation, generally more relevant at larger scale or for organizations with more formal security and compliance requirements than a straightforward .env file approach provides.

Conclusion

Separating secrets into a .env file, excluded from version control via .gitignore, is a simple, foundational practice that directly prevents one of the most common and consequential mistakes in software development, credentials ending up permanently, recoverably present in a project's git history. The .gitignore exclusion step specifically is what provides the actual protection, and it is the step most commonly forgotten by someone newer to this pattern who correctly creates the .env file but does not realize the additional exclusion step is what makes the separation actually meaningful.

If a secret has ever been committed, rotating the actual credential immediately is the essential, non-optional response, since simply removing the line in a later commit does nothing to address the exposure already present in the repository's history.

HostCloud's VPS and hosting environments support standard environment variable configuration for your application's secrets and environment-specific settings, and our support team can help set this up correctly for your specific project structure. See plans at https://hostcloud.in.

Related posts