Skip to content

Separate development/production environments for WordPress

When you’re out Googling on how to maintain a separate development environment for a WordPress installation, you will only stumble across information about how to install all kinds of WAMPP packages. Well, I don’t care about WAMP (or WAMPP). I want to be able to edit my theme, change my plugins, mess with my database locally and then deploy my changes when they’re ready and well-tested (as if I ever…)

Rails was the obvious inspiration for how to do this properly. In Rails, the whole development and deployment process is very intuitive and powerful. In WordPress documentation I never even see the awareness of the need for this separation. They usually tell you to download stuff, upload it and muck about with it on the life production server. But, I’m not the mucking-about-in-live-configurations type. I’m the I-fucked-this-up-so-often-I-want-a-staging-area type. This post is about how I managed to fulfill this wish with WordPress.

Changing the environment

The first thing I had to do was to find some way to decide which environment to go into. For some reason I decided to use Apache’s mod_rewrite to set an environment variable based on the HTTP Host header. This is in fact very illogical, but we’ll get to that later.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteCond %{HTTP_HOST} =bsblog [OR]
RewriteCond %{HTTP_HOST} =bsblog.molensteen
RewriteRule . - [env=WP_ENV:development]
RewriteCond %{HTTP_HOST}
RewriteRule . - [env=WP_ENV:production]
# Keep out of WP's own block of rewrite rules below
# BEGIN WordPress 

. - looks like a needle because it’s voodoo. The dot says I match anything and the dash says I change nothing of what I match. I do set an environment variable to whether I want to be in development or in production.

So I now have an Apache environment variable available for querying from within PHP (as if PHP doesn’t have a $_SERVER['HTTP_HOST'] variable:-? ) and I can make use of that in my wp-config.php.

Multiple configurations in one file

I love configuration files that share the program’s language; wp-config.php being simple PHP code is what make this whole thing so easy:

if ( getenv('WP_ENV') == 'production' ) {
  // ** MySQL settings ** //
  define('DB_NAME', 'blog');             // The name of the database
  define('DB_USER', 'wordpress');        // Your MySQL username
  define('DB_PASSWORD', '[my password]'); // ...and password
  define('DB_HOST', 'bigsmoke.db');        // 99% chance you won't need to change this value
  define('WP_SITEURL', '');
elseif ( getenv('WP_ENV') == 'development' ) {
  // ** MySQL settings ** //
  define('DB_NAME', 'bsblog');          // The name of the database
  define('DB_USER', 'root');            // Your MySQL username
  define('DB_PASSWORD', '[my password]'); // ...and password
  define('DB_HOST', '');       // 99% chance you won't need to change this value
  define('WP_SITEURL', 'http://bsblog');
  //define('WP_DEBUG', true);
define('WP_HOME', WP_SITEURL);
// You can have multiple installations in one database if you give each a unique prefix
$table_prefix  = 'wp_';   // Only numbers, letters, and underscores please!
// The rest of the stuff in this config file just isn't interesting

There’s a few things to note here. You have to use getenv() or $_SERVER instead of $_ENV because variables set by Apache end up in the former two. Another thing to note is that I should have just checked $_SERVER['HTTP_HOST'] instead of resorting to mod_rewrite voodoo. For the rest it’s all very straight-forward: I make some database settings depend on which environment I’m in and I set the URL accordingly.

Development URLs

I had some trouble putting the pieces back together when newer WordPress versions started doing automatic redirects for URL that didn’t match siteurl in the wp_options. This change meant that when going to http://bsblog/ (the development URL for this weblog) for example, I’d inevitably end up at

Links had always been constructed according to this setting, so I had already been planning a plug-in to transform production URLs to development URLs. But, I learned (a little late) that this is completely unnecessary since wp-config.php supports the configuration of a base URL. I had wrongly assumed that settings that weren’t in the sample config file, simply didn’t exist.

Thus, after adding WP_SITEURL and WP_SITEHOME to wp-config.php, everything was working.

Ideas to further enhance your configuration

  • Don’t limit yourself to one development environment if you have more than one development server.
  • Automate your deployment process. I use rsync for this.
  • Write a script to clone your production database to your development database. There’s no substitute for actual data.

3 Comments ( Add comment / trackback )

  1. [...] In Giving WordPress Its Own Directory in the WordPress Codex, it is suggested to change the “siteurl” and “home” options through the administration panel. In my case they would have to be changed to “; and;. I couldn’t do this because I override these with WP_SITEURL and WP_HOME in my wp-config.php. This is because I configured WordPress to support a development environment separate from the live production environm…. [...]

  2. (permalink)
    Comment by Luis
    On November 19, 2009 at 05:47

    Isn’t it simpler to just do:

    if ( $_SERVER['REMOTE_ADDR'] == '' ) {
    } else {

    Instead of setting the environment variable through apache though?

    ANyways, thanks for the writeup.

  3. (permalink)
    Comment by Rowan Rodrik
    On November 14, 2010 at 18:52

    Hi Luis,

    It is much simpler to do it as in your example, but I just somehow got stuck on playing with environment variables and needlessly complicated rewrite rules.

    In two more recent projects I do it very much like you advertise.

    Thanks for your comment.

Post a comment


Your email is never published nor shared.

Allowed HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>