2009
As my first order of business, I want to share my experience setting up wordpress because it was frustrating and I think I have found an elegant solution to this commonly encountered problem. On our server, we will be hosting many different blogs for many different users. Each blog is completely separate, with separate themes and plugins, but for the sake of easily maintaining and upgrading wordpress, we need to use a single wordpress installation. Many users have made different solutions for this problem and now I’ll give an overview of them before I give my own.
- WordPressMU – WordPressMu is not really right for this situation, as it is really built to make administering many blogs from one place easy, for example a blogging network, that has shared themes and plugins. It provides tools for the uber administrators to administer all of the blogs in the network.
- Single database – some solutions have all of the content in a single database and use some hacky stuff or a plugin to choose which content to display on each site. This isn’t smart for more than just a few blogs, and just seems dumb in general. This also doesn’t keep the plugins and themes separate. It really is just for displaying subsections of a blog on different webpages or sites. If you’re in a situation where you want to share plugins, themes, and users between just a few blogs, this might make sense (Example: WordPress Hive)
- Dynamic database switching – some solutions modify the wp-config.php file to figure out which database to connect to using the url, and then change the database connection info and the wp-content directory. This works pretty well, but requires all the blog content being in some standard location like /var/www/vhosts/URLNAME/blog or hardcoding all the location info somewhere. One of these seemed pretty good, well thought out, and fairly flexible, but it seemed more complicated than necessary, adding a lot of configuration and code (Virtual-Multiblog). This could work, and I think I could have kept hacking away to get something more flexible, but I wanted something easier and more flexible. The real problem with this solution though is that all of the vhosts have to point to the main wordpress installation and then wordpress figures out what to do. I wanted to just leave them pointing wherever they normally are, wherever that may be (for example on my server its /var/www/vhosts/theoephraim.com/)
- Symbolic links – Some solutions I found use symlinks to basically recreate the wordpress directory, leaving the wp-content directory as a real directory and changing the wp-config file to include the real copy for the specific website in a specific location (see this). This solution seemed good, except having to create many symlinks, and if a future version of wordpress changes something, it could mean the links might get out of sync. There were some other that involved symlinks but they were kinda hacky and had some weird circular links going on which seemed dumb.
My Solution: symbolic links & smart mod_rewrite rules
I wanted something I could easily put in any folder on my server, regardless of the setup and just have it work as expected, without any complicated changes to anything and complete flexibility on the structure of where the blog lives. It’s a tall order, but we had done something similar for our own cms and for drupal so I figured it must be possible. The solution uses a single symbolic link to the wordpress installation and smart htaccess redirects. This could work if the blog lives in a folder, or a subdomain, or however your server is set up, but this is probably only going to work if you have complete control over the server because you need to make symlinks, change htaccess files, and edit some apache settings for the domain. There are a lot of steps, but really they are VERY easy.
DISCLAIMER: I am by no means an expert on server administration, so PLEASE let me know if you see a problem with what I did, or have any changes or fixes, but I really think I figured this out and it’s better than anything else I’ve seen. And obviously I don’t take responsibility for breaking your server or opening up security holes or anything. Use this tutorial at your own risk. I’m actually not a very good linux geek and have to look up every command. For that reason, I included a lot of examples, because I know it’s easy to forget. But if you don’t know what youre doing, you probably shouldn’t be setting up wordpress in this way so just go back to wordpress.com and set it up with their instructions.
Step 1: Download and unpack wordpress
Download wordpress somewhere to your server (for example /var/www/wordpress). Example:
- wget http://wordpress.org/latest.tar.gz
- tar -xzf latest.tar.gz
- mv wordpress /var/www/
- rm -f latest.tar.gz
Step 2: Create symbolic link to the global install from your desired blog location
Wherever the blog is going to live, create a symbolic link to wordpress. Example:
- cd /var/www/vhosts/theoephraim.com/httpdocs
- ln -s /var/www/wordpress wordpress (Make sure you use -s which makes the link symbolic, and make sure there is no trailing slash on the target
Step 3: Allow cross-site access to the global install
You must change the vhost basedir restrictions so that your site can access the files because they are not within the scope of what they can normally access. Makes sense right? Otherwise your site could access files in someone else’s (which is exactly what we are trying to do, but we must control it). For me, this involves changing the vhost.conf file for the specific site and then making apache notice my change. If each site doesnt have its own vhost.conf file you might have to just add it to the main apache conf file. I made my vhost.conf file look like this, but it may have other settings as well. You can probably leave them alone and just add this, obviously removing the stuff in (parentheses)
<Directory /var/www/vhosts/theoephraim.com/httpdocs/blog> php_admin_flag safe_mode off php_admin_value open_basedir /var/www/vhosts/theoephraim.com:/var/www/wordpress:/tmp </Directory>
To make the change take effect, on Plesk, I use this command:
/usr/local/psa/admin/sbin/websrvmng —u ——vhost-name=<DOMAIN NAME>
Or to make life simple, you can just restart apache, which you can usually do with this command:
apachectl restart
Step 4: Create a .htaccess file with smart rewrite rules
This whole method is based around redirecting requests to the blog url into the the symlinked wordpress directory. Be forewarned, there is one weird gotcha at the end of this section so make sure you read where it says “IMPORTANT!” and also, please note that I haven’t tested this updated script that works with permalinks if the blog is within a subfolder. I think the answer has to do with the Rewrite Base line but can’t be bothered to dig back into .htaccess hell right now. If anyone figures it out, please share. So anyway, here’s where most of the magic happens:
Options +FollowSymLinks
RewriteEngine on
RewriteBase /
#if its not a real file or directory, try in the wordpress directory
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !^/wordpress/(.*)$
RewriteRule ^(.*)$ /wordpress/$1 [L]
#if its still not found, just forward to wordpress/index.php (this enables permalinks)
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . wordpress/index.php [L]
#This rule doesnt seem to work for me... Anyone know why? RewriteRule ^index.php$ wordpress/index.php
So lets review whats going on here:
- First we turn on follow symlinks and the rewrite engine (standard mod_rewrite stuff)
- That first rule checks to see if the request is for something that exists and only if it DOESN’T exist, rewrites the request to add wordpress/ at the beginning. Examples:
- url.com/file1.php -> url.com/file1.php (if it exists)
- url.com/file1.php -> url.com/wordpress/file1.php (if it doesnt exist)
- The second rule says if it’s still not found, just try wordpress/index.php in order to enable permalinks to work.
- IMPORTANT!!!! The third rule then says if the request is just for nothing (example.com/) then to serve up index.php as well. Now for some reason, this only works if there exists a file called index.php! If it doesn’t exist, I get an apache error page that says I havent uploaded any content to my site yet. So, very important, create an index.php file. You can use the touch command (it just creates an empty file)
Step 5: Edit the global wp-config.php file
Now a request to a wordpress file is being routed to the wordpress directory, but the configuration info still lives in the site’s directory so now we have to change the main wordpress config file to include the real config file for the specific blog instance. In the case that the blog is going to live in the root directory of a site, this is easy, but when it’s in a subdirectory, it gets slightly more complicated. Heres my wp-config.php file:
<?php
// when the blog is in the root directory of the server, this first case works
if ( !@include( $_SERVER['DOCUMENT_ROOT'] . '/wp-config.php' ) ) {
// if the include fails, the blog is in a subdirectory so $_SERVER['DOCUMENT_ROOT'] is incorrect
$wprootdir = substr($_SERVER['SCRIPT_NAME'], 0, strpos($_SERVER['SCRIPT_NAME'], '/wordpress/'));
//this finds the directory name by figuring out where "/wordpress/" was inserted into the script path
require_once( $_SERVER['DOCUMENT_ROOT'] . $rootdir . '/wp-config.php' );
}
?>
Step 6: Create the real wp-config.php file for your blog
Now we need to create the real wp-config file for the individual site. This is the file that is getting included from the global file above. It needs to live at the same level as the wordpress link. Heres my file, but you can consult the wordpress docs for editing your own file. The last 4 lines are important and whats special here:
<?php define( 'DB_NAME', '<database name>' ); define( 'DB_HOST', 'localhost' ); define( 'DB_USER', '<db username>' ); define( 'DB_PASSWORD', '<db passwd>' ); define( 'AUTH_KEY', 'my auth key' ); define( 'SECURE_AUTH_KEY', 'my auth key' ); define( 'LOGGED_IN_KEY', 'my auth key' ); define( 'NONCE_KEY', 'my auth key' ); define( 'WP_CONTENT_DIR', $_SERVER['DOCUMENT_ROOT'] . $wprootdir . '/wp-content' ); define( 'WP_CONTENT_URL', 'http://theoephraim.com/wp-content'); define( 'ABSPATH', $_SERVER['DOCUMENT_ROOT'] . $wprootdir . '/' ); require_once( ABSPATH . 'wp-settings.php' ); ?>
I was considering just writing absolute paths instead of using the $_SERVER['DOCUMENT_ROOT'], but I figured wordpress is using it somewhere else so I wanted to make sure it works.
Step 7: Copy the wp-content folder (or link it if you want to)
Now if you want the blog to have its own themes and plugins, you need to copy the wp-content folder from the global install to the blog folder at the same level as the wordpress link. Alternatively, if you wanted to have some blogs use a shared repository of themes and plugins, you could instead create a symlink to the folder that is living in the global install directory (haven’t tested this, but should work). Example:
- cd /var/www/vhosts/theoephraim.com/httpdocs
- cp -r /var/www/wordpress/wp-content ./
Step 8: Set up WordPress!
Everything should be good to go! Just go to the install script (for example http://theoephraim.com/wp-admin/install.php) and run the installation! (If you didnt create your database already, you’ll have to do that now and update your config file.
Review
So lets just review. We use mod_rewrite to route requests to our blog through a symlink to a shared wordpress install. In order to do that, we have to change the apache settings to allow the site to access it (change the basedir restrictions). We then make the shared wp-config.php file smart and go back into the specific blog’s folder using the request URI and document root in order to include the real wp-config.php file. In that file, we specify our normal settings and the location of the wp-content folder.
Here’s the file structure we created:
- /var/www/wordpress
- wp-config.php
- all the normal wordpress files
- …
- /var/www/vhosts/theoephraim.com/httpdocs (this is the path to my blog, it could be totally different and could include subdirectories after the base directory
- .htaccess
- wp-config.php
- wordpress -> this is a symbolic link to /var/www/wordpress
- wp-content
- wp-themes
- wp-plugins
- index.php (this file actually doesnt do anything, but I didnt want to omit it for fear of confusing someone)
Conclusion
Using this system, creating a new wordpress blog just means making a symlink, and copying 3 things. The wordpress files are totally unchanged. This method should be pretty future-proof since only one symlink is made. Updating wordpress on many blogs can now be done just once. I spent an entire day figuring this out and writing this tutorial, but please if you see an error, or a suggestion, please let me know. I’d REALLY rather not help you debug setting this up on your server, but if there’s something that was unclear or you figured out how to get around some roadblock, please share so I can make this tutorial better. Enjoy!
Leave A Comment
powered by