Introduction
On a classic WordPress install, there is a configuration file called wp-config.php
that you must fill with your environment specific information, such as your database credentials, to make the site work.
By default it looks like this :
/**
* The base configuration for WordPress
*
* The wp-config.php creation script uses this file during the
* installation. You don't have to use the web site, you can
* copy this file to "wp-config.php" and fill in the values.
*
* This file contains the following configurations:
*
* * MySQL settings
* * Secret keys
* * Database table prefix
* * ABSPATH
*
* @link https://wordpress.org/support/article/editing-wp-config-php/
*
* @package WordPress
*/
// ** MySQL settings - You can get this info from your web host ** //
/** The name of the database for WordPress */
define( 'DB_NAME', 'database_name_here' );
/** MySQL database username */
define( 'DB_USER', 'username_here' );
/** MySQL database password */
define( 'DB_PASSWORD', 'password_here' );
/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
/** Database Charset to use in creating database tables. */
define( 'DB_CHARSET', 'utf8' );
/** The Database Collate type. Don't change this if in doubt. */
define( 'DB_COLLATE', '' );
/**#@+
* Authentication Unique Keys and Salts.
*
* Change these to different unique phrases!
* You can generate these using the {@link https://api.wordpress.org/secret-key/1.1/salt/ WordPress.org secret-key service}
* You can change these at any point in time to invalidate all existing cookies. This will force all users to have to log in again.
*
* @since 2.6.0
*/
define( 'AUTH_KEY', 'put your unique phrase here' );
define( 'SECURE_AUTH_KEY', 'put your unique phrase here' );
define( 'LOGGED_IN_KEY', 'put your unique phrase here' );
define( 'NONCE_KEY', 'put your unique phrase here' );
define( 'AUTH_SALT', 'put your unique phrase here' );
define( 'SECURE_AUTH_SALT', 'put your unique phrase here' );
define( 'LOGGED_IN_SALT', 'put your unique phrase here' );
define( 'NONCE_SALT', 'put your unique phrase here' );
/**#@-*/
/**
* WordPress Database Table prefix.
*
* You can have multiple installations in one database if you give each
* a unique prefix. Only numbers, letters, and underscores please!
*/
$table_prefix = 'wp_';
/**
* For developers: WordPress debugging mode.
*
* Change this to true to enable the display of notices during development.
* It is strongly recommended that plugin and theme developers use WP_DEBUG
* in their development environments.
*
* For information on other constants that can be used for debugging,
* visit the documentation.
*
* @link https://wordpress.org/support/article/debugging-in-wordpress/
*/
define( 'WP_DEBUG', false );
/* That's all, stop editing! Happy publishing. */
/** Absolute path to the WordPress directory. */
if ( ! defined( 'ABSPATH' ) ) {
define( 'ABSPATH', __DIR__ . '/' );
}
/** Sets up WordPress vars and included files. */
require_once ABSPATH . 'wp-settings.php';
But a project rarely ever lives on a single host.
Most of the time, it starts on a developer’s machine, then is continued on another developer’s machine, then is deployed to a staging site, then a production one.
That means in essence that each of these environments should have their own wp-config.php file so we can specify credentials specifically for each of them.
Issues with the wp-config.php file management
In the past, I’ve noticed that the use of this file could cause issues and confusion with developers, because it’s not that straight forward to manage.
Case 1: when included in deployments
Some of them chose to version it, and also deploy it, because it’s a required WordPress file, that has some logic in it besides the configuration lines ( the last require
for example). Without this file, WordPress doesn’t load your site normally and executes it in install mode instead.
To prevent this, developers chose to deploy this file everywhere the site needs to be installed. And then they added some sort of conditional logic saying :
- “If it’s my machine, then the database user is this one”
- “If it’s the staging env, then the database user is this one”
- “If it’s the production env, then it’s this one.”
This is a recurring issue in credentials management in an application, it’s not WordPress specific. It’s also a common mistake among junior devs to go for this tower of credentials pattern.
This is not sustainable in the long run. It will become harder and harder to maintain as you add developers or environments. It’s also a security flaw because this configuration file will eventually contain the database user and passwords of all your team.
Case 2: when excluded from deployments
Another strategy consists in not deploying the wp-config.php file.
As it’s required by WordPress, two things can happen :
- If you have the install mode available, your site will run in install mode.
- If you don’t, it won’t run at all.
In both cases though, you will have to create one, either by following the install wizard or by creating it manually. This file will be specific for the host it runs on, which is already an improvement from the case n°1.
Improvements
There are four things I’d like to improve from this :
- I’d like to separate the config information from the logic.
- I’d like to use .env files
- I’d like to store configuration information out of my WordPress code, and ideally above the site document root.
- I do not wish to rely on the installation wizard. More precisely, I do not wish to deploy either the wp-config-sample.php or the install folder in staging and production environments for security reasons.
To achieve this, let’s see how the Bedrock architecture can help us.
Bedrock
Bedrock presents itself as a WordPress boilerplate with modern development tools, easier configuration, and an improved folder structure.
It’s an alternative architecture, which has all the improvements I was looking for.
Here’s what it brings compared to a classic WordPress install :
Bedrock | Standard WordPress | |
---|---|---|
Separate configs per environment | ✅ | ❌ |
Environment variables | ✅ | ❌ |
Custom wp-content directory | ✅ | ❌ |
Composer for managing WordPress installation | ✅ | ❌ |
Composer for managing WordPress plugins and themes | ✅ | ❌ |
mu-plugins autoloader | ✅ | ❌ |
I love many things about Bedrock, and I encourage you to check it out to discover more if you don’t know this tool already, but what’s mostly interesting in our case is the line about environment variables.
Multi-environment configuration with Bedrock
Under the hood
Bedrock changes the wp-config.php file like so :
/**
* Do not edit this file. Edit the config files found in the config/ dir instead.
* This file is required in the root directory so WordPress can find it.
* WP is hardcoded to look in its own directory or one directory up for wp-config.php.
*/
require(dirname(__DIR__) . '/vendor/autoload.php');
require_once(dirname(__DIR__) . '/config/application.php');
require_once(ABSPATH . 'wp-settings.php');
The second require
loads a separate configuration file. We can see that its path is above the site’s document root, which is an improvement.
If we look into the config/application.php file, we find lines that used to be in wp-config.php, but slightly modified :
define('DB_NAME', env('DB_NAME'));
define('DB_USER', env('DB_USER'));
define('DB_PASSWORD', env('DB_PASSWORD'));
define('DB_HOST', env('DB_HOST') ?: 'localhost');
For example, above, the lines regarding the database connection now look for environment variables instead of PHP values, which means you need to create a .env file to store those variables, which is something I wanted.
PHP dotenv is used to load the .env
file. All variables are then available in your app by the built-in getenv
, $_SERVER
, or $_ENV
methods. However, bedrock uses the env library and its env
function which handles simple type coercion (such as converting the string 'True'
to the boolean true
). We can see the env function being used in the application.php file example below.
In practice
Thanks to its architecture and loading mechanisms, bedrock puts me in a position where I can create and fill a .env file at the root of my WordPress project. Here’s how we use it :
- We create a .env file at the root of our project (where Bedrock looks for it).
- We ignore this .env file path from our source control by adding it to the .gitignore file.
- We create a model (for example .env.example), which serves as a blueprint to copy /paste for our colleagues, then add this model file to your source control.
- We add a section in your readme.md file explaining how the .env file should be created from the .env.example one upon project installation.
Environment types
Besides the use of environment variables, Bedrock allows us to specify the environment type we are running between development
, staging
, and production
.
This is interesting because it permits us to specify different configurations based on the type. For example, we could say that we allow to display errors in development environments, but not in production ones, where we’ll use logging instead.
Conclusion
Specifying credentials in configuration files is something developers will do for each installation of your projects, so easing the multi-environment capabilities of a WordPress install is a great way to improve their experience. Bedrock standardizes and simplifies this process greatly, and it’s just one of its many added benefits.
If you found this article interesting, I encourage you to follow my work via the form below. I mainly write about production teams' optimization, processes, management, and the tooling around those subjects. I’m even preparing an online course to concentrate all of this for you in a unique place. I hope it can help you get the very best out of your development team.
Follow my work
Follow me to get notified of new posts, resources, and online courses I create.