Installing WordPress As A Subversion Checkout ( Or External ) In A Subdirectory

My preferred method for installing WordPress is to check it out from the Subversion repository (http://svn.automattic.com/wordpress/). Recent versions of WordPress have added support to allow wp-config.php and wp-content to live outside of the WordPress directory. Here’s the steps to make your setup work as a Subversion check out (based on Sam‘s WordPress Plus Subversion slides) in a subdirectory:

  • Assumptions:
    • Domain is example.com
    • Installing in root directory of the domain: /home/example.com/public_html/
    • Web server and MySQL database/username already setup and working correctly
  • cd /home/example.com/public_html/
  • mkdir wp
  • svn co http://svn.automattic.com/wordpress/branches/2.7/ wp (could be any branch, tag or -trunk)
  • Install WordPress at http://example.com/wp/
  • mv wp/wp-config.php wp-config.php (moves wp-config.php from /home/example.com/public_html/wp/ to /home/example.com/public_html/)
  • mkdir wp-content
  • Add WP_CONTENT_DIR and WP_CONTENT_URL after WP_LANG to wp-config.php:

    ...
    define ('WPLANG', '');
    
    define( 'WP_CONTENT_DIR', dirname( ABSPATH ) . '/wp-content' );
    define( 'WP_CONTENT_URL', 'http://example.com/wp-content' );
    
    /* That's all, stop editing! Happy blogging. */
    ...
    
  • cd wp-content
  • mkdir plugins
  • cd plugins
  • ln -s ../../wp/wp-content/plugins/akismet .
  • cd ..
  • mkdir themes
  • cd themes
  • ln -s ../../wp/wp-content/themes/default .
  • Update WordPress settings in wp-admin:
    • Settings -> General -> WordPress address (URL): http://example.com/wp/
    • Settings -> General -> Blog address (URL): http://example.com/
  • Permalinks:
    • Settings -> Permalinks -> Custom Structure: /archives/%year%/%monthnum%/%postname%/ (my preferred format, you can choose your own if you’d like)
    • .htaccess:
      RewriteEngine On
      RewriteBase /
      
      RewriteRule ^wp/wp-content/(.*)$ wp-content/$1 [R=301,L]
      RewriteRule ^wp-admin/(.*)$ wp/wp-admin/$1 [R=301,L]
      RewriteRule ^$ /wp/index.php [L]
      
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteRule . /wp/index.php [L]
      

You can update WordPress at any time by running ‘svn up’ in /home/example.com/public_html/wp/. Updating when a new major release comes out means switching branches, for 2.8 that would ‘svn switch http://svn.automattic.com/wordpress/branches/2.8/’.

When logging in remember that wp-admin is at http://example.com/wp/wp-admin/.

I’ve also run into a few plugins that need fixing, because they lookup the WP URL via get_bloginfo( ‘wpurl’ ) instead of get_bloginfo( ‘home’ ). The problem with ‘wpurl’ is it returns http://example.com/wp/, making it reference the wp-content directory that came with WordPress (http://example.com/wp/wp-content/) instead of the one that you are actually using at http://example.com/wp-content/. The wp-content redirect in the .htaccess file should help make this a bit less painful by redirecting /wp/wp-content/ requests to /wp-content/.

If you’ve setup /home/example.com/public_html/ as it’s own Subversion repository you can make WordPress an external for the /home/example.com/public_html/wp/ directory. That way you’ll still be able to have a pristine setup of WordPress while being able to manage wp-config.php, themes and plugins in your own repository.

20 Comments

  1. I suspect this will work with Apache Alias configuration.

    Web site: /home/example.com/public_html/
    WP content: /home/example.com/wp-content/

    In your apache config file, add the following line within the tag:

    Alias “/wp-content” “/home/example.com/wp-content/”

    AllowOverride None
    Options None
    Order allow,deny
    Allow from all

    Then in your wp-config.php you can do…

    define( ‘WP_CONTENT_DIR’, ‘/home/example.com/wp-content’ );

    Most people do not have the ability to edit their apache config files, but if you do this is a wonderful alternative.

    Here’s another tip, plugins shouldn’t be executed directly, but occasionally a hacker will try to access them directly. A useful rule to add to the .htaccess to block direct php access would look something like this:

    RewriteRule /wp-content/.*.php.* – [F]

  2. There are some resources that plugins include that they need to be able reference, generally this is limited to CSS and images.

  3. Right, that’s why you only want to block direct requests that include a .php in them. CSS, images, swf, etc.. should be able to pass through with the rewrite rule above. The comment I typed got crammed into one big cluster (foul word here). Hopefully the concept came across ok. 🙂

  4. This seems fairly concise, is there a Codex page which has the same sort of guide, and if not could you make one?

    (Nested comments look funky.)

  5. Blocking .php files would cause problems for plugins that want to provide additional services, like new wp-admin sections and AJAX features.

  6. I think there are various pieces of this information in different parts of the Codex, not sure that it’s available in one spot (which is why I went through Sam’s slides put this together). I’ll look again and add this to the Codex if it’s not already there.

    The threaded comments are still not right, at some point I’ll play with the styling some more.

  7. Hi Joseph,

    I believe 2.8 is still not updated in svn right?

    How do I get my hands on it?

  8. 2.8 won’t be out until probably late March/early April. You can use what will become 2.8 by checking out -trunk from svn.

  9. Cool… that sounds good. I’ll get to work on that. Thanks Joseph 🙂

  10. Excellent instructions. One remark: in the line that specifies WP_CONTENT_DIR, there is one ) missing at the end of the line, and the ( after ABSPATH should be deleted.

  11. Thanks for pointing that out, fixed now.

  12. Sorry, my fix was wrong.

    What now is:

    define( ‘WP_CONTENT_DIR’, dirname( ABSPATH . ‘/wp-content’ ) );

    should be:

    define( ‘WP_CONTENT_DIR’, dirname( ABSPATH ) . ‘/wp-content’ );

    as you need the dirname of abspath only.

  13. If you change this line:

    RewriteRule ^wp-admin/(.*)$ wp/wp-admin/$1 [R=301,L]

    to:

    RewriteRule ^wp-admin(.*)$ wp/wp-admin$1 [L]

    then your admin is also available as wp-admin instead of wp/wp-admin

  14. I think I’d probably make it an optional match instead.

    Any particular reason for removing the 301 redirect too?

  15. I could kiss you.

    Just thought I’d add the svn error message here so it’s googleable:

    svn: Directory ‘wp-content’ is missing

  16. At what point did you see that error?

  17. You are right, blocking PHP is problematic for some plugins that provide additional services that use links directly to the plugin directory to execute PHP. These plugins most all include the following line:

    require_once(‘../../../wp-config.php’);

    Which means you cannot do what I was suggesting with using the Alias directive in Apache without creating an additional wp-config.php. Essentially, where ever your wp-content folder is located, a wp-config.php file must also be located for those plugins that try to include it.

    To get my example to work, you would need to create a wp-config.php file in the same directory where you put the wp-content folder. In this wp-config.php, you would add a require_once function call with the absolute path where your actual wp-config.php is located. If you installed via subversion to /home/example.com/public_html/, then…

    I’m in the process of moving a site to a new server and will be testing this out. Either way, I’m quite pleased the defines WP_CONTENT_DIR and WP_CONTENT_URL are available to us.

  18. Plugins and themes should never, ever, EVER, include wp-config.php, wp-load.php, wp-blog-header.php, etc. directly. It will break on some WP installs, just no two ways around it.

    From what I’ve seen themes & plugins do this because they want to provide custom CSS and AJAX hander with data from the database. Using the query_vars hook or the built in AJAX handlers in WP are much better way of doing this, as they will work for all WP installs.

  19. I agree. If all plugins used the query_vars hook or the built in AJAX handlers in WP rather than the include of the wp-config.php, then you could do what I originally said and block php execution for all files and folders in the wp-content folder, which would result in a much more secure WordPress installation.

  20. I’m happy to report that the Alias technique with locating the wp-content in an entirely different folder than where the WordPress subversion checkout is located works perfectly for my setup. None of the plugins I am using include the wp-config.php in them, so I don’t need the hack I described in a previous reply.

    My folders look like this:

    /home/username/example.com/htdocs (where I do the svn co wordpress)
    /home/username/example.com/wp-content (where I map my wp-content to)

    Apache Alias entry looks like this:

    Alias “/wp-content” “/home/username/example.com/wp-content”

    AllowOverride None
    Options None
    php_admin_flag engine off # optional

    defines added to wp-config.php:

    define( ‘WP_CONTENT_DIR’, ‘/home/username/example.com/wp-content’ );
    define( ‘WP_CONTENT_URL’, ‘http://example.com/wp-content’ );

    With this setup, the writable folders for my WordPress installation cannot execute PHP directly, which is a huge security benefit of using this technique. This may not work for everyone. If your open_basedir setting is set in PHP this may not work for you.

    Anytime I install a new plugin, I first grep the source to make sure it doesn’t include the wp-config.php. If a plugin does include the wp-config.php anywhere, that’s an indication that the PHP in that plugin must execute directly, in which case you will have to remove the php_admin_flag engine off line above and add the hack I described in the last reply and put a wp-config.php in the /home/username/example.com/ folder.

Leave a Reply

Your email address will not be published. Required fields are marked *

© 2018 Joseph Scott

Theme by Anders NorénUp ↑