No matter how small your business is, if you make websites you work in multiple environments. If you’re a freelancer working alone chances are that for each project you work on you have at least two environments, possibly three. One on your local machine, one for client previews, and maybe a further one for production.
If you work as part of a team it can get even more complicated. In my office we can have up to three people contributing on a job, each with their own local environment settings. Plus, depending on the needs of the client, there could be up to four server environments set up. Throw source control into the mix (and you really should be using source control) and you end up with a small headache just keeping track of whats going on.
In this post I’m documenting a method of working with WordPress and multiple environments but the same concepts can be applied across a range of other content management systems such as ExpressionEngine and Joomla or frameworks like CodeIgniter.

Fig 1. The different local and server environments.
Overview of the different environments
- The development environment is for testing settings on your server. It helps to test for problems which would be server specific. This is only for you or your team, the client never sees it.
- The preview environment is for allowing your client to preview the current work in progress without having to give them access to the source code.
- Staging is for testing before deployment to production and is hosted on the clients servers. You put the site up here towards the end of the project when you are happy letting them have access to the source code. If you have an ongoing retainer with the client you might ditch the preview environment and only use this.
- Production is the live environment. The site is deployed here when it has been fully tested and is ready to be seen by the world.
The development cycle
- The developer works on a feature in their local environment.
- When the feature is ready, the developer commits it to the source control and deploys it to development server to test it.
- When the feature is reviewed and tested it is deployed to preview server.
- The client then previews and you go through a number of revisions.
- When the client is happy they sign off and it is deployed to staging.
- Then on launch day, the site is deployed to production.
The problem with this setup and WordPress
The problem with the way WordPress is set up is that it has only one configuration file (wp-config.php) which allows you to specify your database details and a few other options for a single environment.
Say you’re finished working on a feature and you want to put it onto a preview site and allow the client to look at it. You create the webspace and deploy the latest revision from your source control. The wp-config file that gets uploaded to your preview server will still have the details for connecting to your local database meaning it won’t work. Changing these details to point to the servers database obviously means that then your local site won’t work. You can get around this by deploying, logging into the server through FTP and changing the settings manually but this isn’t really an ideal solution as theres still the chance it could be overwritten by accident through a future deployment.
Configuration differences across environments
- Database configuration.
- Error reporting, it should be turned on on development servers and off on preview, staging and production.
- Robots need to be blocked from development, preview and staging environments. Having Google display links to your work in progress is not just embarassing, duplicate content will hurt your clients SEO.
- Keeping unauthorised people from accessing the development and preview environments.
The Solution
Enter the solution, the multi-environment wp-config bootstrap. This is a custom file that replaces the wp-config file in your installation and helps you configure your different environments.
How it works:
This works by accessing the $_SERVER['SERVER_NAME'] variable which gives us the server name. The environments each have a unique URL which gives them their own unique itentifier that will allow us differentiate between them, something similar to this:
- Local: http://localhost/project (“localhost”)
- Development: http://dev.yoursite.com (“dev.”)
- Preview: http://preview.yoursite.com (“preview.”)
- Staging: http://stage.clientsite.com (“stage.”)
- Production: http://clientsite.com (none of the above, which is also unique!)
Then we define a constant variable giving us an easy way to check the environment throughout the application. This all works like this:
// Define Environments
$environments = array(
'local' => '.local',
'development' => '.dev',
'staging' => 'stage.',
'preview' => 'preview.',
);
// Get Server name
$server_name = $_SERVER['SERVER_NAME'];
foreach($environments AS $key => $env){
if(strstr($server_name, $env)){
define('ENVIRONMENT', $key);
break;
}
}
// If no environment is set default to production
if(!defined('ENVIRONMENT')) define('ENVIRONMENT', 'production');
Adding your database connection details
Now all we need to do to add the different database configuration details. I’m using a switch here but an if statement will work just as well.
// Define different DB connection details depending on environment
switch(ENVIRONMENT){
case 'local':
define('DB_NAME', 'bootstrap');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');
define('DB_HOST', 'localhost');
define('WP_DEBUG', true);
define('WP_SITEURL', 'http://bootstrap.local/');
define('WP_HOME', 'http://bootstrap.local/');
break;
case 'development':
define('DB_NAME', 'bootstrap');
define('DB_USER', 'root');
define('DB_PASSWORD', 'root');
define('DB_HOST', 'localhost');
define('WP_DEBUG', true);
break;
case 'staging':
define('DB_NAME', 'database_name_here');
define('DB_USER', 'username_here');
define('DB_PASSWORD', 'password_here');
define('DB_HOST', 'localhost');
break;
case 'preview':
define('DB_NAME', 'database_name_here');
define('DB_USER', 'username_here');
define('DB_PASSWORD', 'password_here');
define('DB_HOST', 'localhost');
break;
}
// If database isn't defined then it will be defined here.
// Put the details for your production environment in here.
if(!defined('DB_NAME'))define('DB_NAME', 'database_name_here');
if(!defined('DB_USER')) define('DB_USER', 'username_here');
if(!defined('DB_PASSWORD')) define('DB_PASSWORD', 'password_here');
if(!defined('DB_HOST')) define('DB_HOST', 'localhost');
if(!defined('DB_CHARSET')) define('DB_CHARSET', 'utf8');
if(!defined('DB_COLLATE')) define('DB_COLLATE', '');
You should create different databases for each environment. However, if you want to use the same database for multiple ones you can define the WP_SITEURL and WP_HOME constants for each one inside the switch. This will hardcode the site URL meaning all your links will work.
Just be careful because if for example you’re using the same database for development and preview, any changes you make to content in development will then be reflected in the preview.
You could also use the same database and customise the table prefix for the environments which would effectively add multiple WordPress installations to the one database.
Lastly it can be set it up to turn on debugging automatically for the local and development environments and off for the others making sure you never accidentally leave it on on your production site.
Other usage examples
Now that we have the ENVIRONMENT variable set up we can add a few handy functions to the theme.
is_production()
A function to check if the current environment is production or not. Add this into your themes functions.php file:
function is_production(){
if(ENVIRONMENT == 'production'){
return true;
}else{
return false;
}
}
Using Less or Sass
If you like to use Less or Sass and only compile the CSS for the production you can add this snippet into your themes header:
<!--?php if(!is_production()): ?-->
<script type="text/javascript">// <![CDATA[
var less = { env: 'development' };
// ]]></script>
<script type="text/javascript" src="<?php bloginfo('template_directory'); ?>/assets/js/libs/less-1.3.0.min.js"></script>
<script charset="utf-8" type="text/javascript">// <![CDATA[
less.watch();
// ]]></script>
<!--?php else: ?-->
<!--?php endif; ?-->
Just make sure you compile the CSS before deploying to production!
Making the site accessible to only logged in users
If you want to make sure only people who are logged in can see the site you can reirect all unlogged in requests to the WordPress login page. Handy for keeping clients from nosing around your development environment. Add this to functions.php:
function password_protected(){
if(!is_user_logged_in() && (ENVIRONMENT == 'development' || ENVIRONMENT == 'staging')){
wp_redirect(get_option('siteurl') .'/wp-login.php');
}
}
add_action('template_redirect', 'password_protected');
Adding a cookie check
A simple way of adding a cookie check to your preview environment that only lets people with a preview cookie view it. The cookie is created by adding ?preview to the URL like this http://preview.yoursite.com?preview
function require_cookie(){
if(isset($_GET['preview'])){
setcookie('preview', 'yes', time()+36000, '/');
header('Location: /');
exit;
}
if(ENVIRONMENT == 'preview' && (!isset($_COOKIE['preview']) || $_COOKIE['preview'] != "yes")){
wp_redirect( 'http://www.google.com', 302 );
exit;
}
}
add_action('init', 'require_cookie');
That will keep the general public out of the site while giving the client an easy way to preview it.
Blocking robots
As explained earlier, keeping crawlers out of your development, preview and staging sites is very important. You can make sure this is done automatically by adding the following snippet:
function robots_access(){
if(is_production() && get_option('blog_public') == '0') update_option('blog_public', '1');
if(!is_production() && get_option('blog_public') == '1') update_option('blog_public', '0');
}
add_action('init', 'robots_access');
And there you have it, a nice professional way to manage your WordPress sites across your environments. This was inspired by Michael Flanagan’s CodeIgniter setup and Leevi Grahams Config Bootstrap for ExpressionEngine. You can download the wp-config file by clicking the link below and if you have any suggestions please leave a comment.
Comments
Mahbubur Rahman on May 22, 2012
Thanks
Saved my hours. I was about to start writing this env specific config for one of our project. Now i don’t have to
gdean on May 22, 2012
Thank you, this is a nice approach. I have been using HTTP_HOST to do something similar and I am now inspired to update my version to include your enhancements.
Do you think there might be any security issues arising from this type of approach?
Abban on May 22, 2012
There shouldn’t be any security issues because all functionality is run on the server side.
I’ve been using this and a more detailed one for ExpressionEngine and CodeIgniter for the last 8 months with no problems.
Dwayne Charrington on July 5, 2012
Very nice! I use a similar approach in all of my WordPress projects as well except I believe you’re repeating yourself unnccesarily here. You’re repeating each constant over and over again. Why not instead assign the different values to variables which are changed in your switch/case statement and supply the variable to the constant? That way you only define it once and the values can change.
Abban on July 5, 2012
I know, the values really should be set in an array that is looped through to define the constants. That’s the way I actually do it on my own projects. For the purpose of this example though I wanted to keep it simple.
I should really add nested comment replies on here too..
Liam Jay on July 19, 2012
Cheers man, this is going to save me a lot of time!
I didn’t realise anything like this was possible, so thanks for sharing with the rest of us.
CharliePops on July 30, 2012
Very helpful, thanks a lot!.
Anthony Torres on October 24, 2012
Something that might be good to add to this bootstrap is a way to provide the user to choose the environment they would like to display via a url parameter.
For example: I have a very small website, I’m the only developer, tester, and everything else, as a result I only have local and production environments. It would be great if I could check to make sure the production specific code behaved the way i would like by changing the environment locally without pushing the code.
I understand that a staging environment is supposed to do this for me but that is a lot over overhead for something as small as what i’m working on.
Shovan Sargunam on November 23, 2012
Thanks man, seems to answer what I was looking for. Let me test it
Shovan Sargunam on November 23, 2012
Great! It works,
I want live, stage and dev to have the same database. Do you know how that can be done?
@shovansargunam
Abban on November 23, 2012
Install WP on your production site so it adds the production URL to the database. Then stick the same DB details in each case in the switch statement and define the WP_SITEURL and WP_HOME as the different environment urls eg:
define('WP_SITEURL', 'http://staging.abandon.ie/'); define('WP_HOME', 'http://staging.abandon.ie/');D.Lo on December 3, 2012
This blog post was SO helpful. Exactly what I was looking for, thanks!
Mathias Aeschlimann on January 15, 2013
nice work. just spotted a typo in the comments …batabase
Abban on January 15, 2013
Batman’s database