Moving the Uploads Directory in WordPressPosted: August 23, 2011
To give context, I will start from the beginning,… If you’ve come here simply looking for any way to move the uploads directory – skip all this and jump straight to Moving the Uploads Directory…
There are many times when its a great idea to move some of the default locations of certain directories in WordPress. At my current job, we have a handful of sites that all share a the same plugins and theme folders. However, when I arrived at this position a few months ago, they had done this by using symbolic links – this causes problems with many plugins due to their use of the __FILE__ magic constant. As a result it was necessary to modify many of the 3rd party plugins we had remove that constants use.
In WordPress we can change the default location of the wp-content folder by defining a couple constants in wp-config.php as explained in the Codex:
define( 'WP_CONTENT_DIR', $_SERVER['DOCUMENT_ROOT'] . '/blog/wp-content' ); define( 'WP_CONTENT_URL', 'http://example/blog/wp-content');
Plugins can be moved similarly as the same section in the Codex explains. What happens here is we are simply telling WordPress where we want to put wp-content or plugins, rather than confusing the issue by having it point to a directory where they wont be via symbolic links.
This is extremely useful, by doing this we can share our plugins and themes across multiple instances of WordPress without having to push code out and maintain each one individually. Moreover, many if not most developers using source control on multiple environments probably already ignore wp-config.php – or at least have some way of distinguishing environments within wp-config.php.
Moving the uploads directory…
While discussing the necessary changes in wp-config with team members, I realized we might run into a hitch with the uploads directory, which resides within wp-content. While we wanted to share plugins and themes, we certainly did not want to share content.
Initial googling resulted in no clear documentation as to how to move the uploads folder out of wp-content – and the codex page describing the the way to move wp-content makes no mention of either themes or uploads. So while I considered the possibility of leaving wp-content where it was, and moving themes and plugins, I could not find a way to move themes out of wp-content. I eventually found that we could filter the wp_upload_dir() function, or that we could update the ‘upload_path’ option – but both of these methods would require a plugin which would need to be maintained in source control, need to be turned on, and need to have an interface to allow a human to add the correct information depending on the environment. This to me seemed like a lot of work for something that started out with 2 lines in wp-config.php – not only that but it makes the entire exercise pointless, as there is already an option in the general settings to manually change it. A manual change is not what we needed. There had to be some other way.
Eventually I made my way through the core code to find out that there is an undocumented constant called UPLOAD which wp_upload_dir() checkes for – if its defined it will used it, if its not it will create a path by combining WP_CONTENT_DIR (which as we saw above, we can define in wp-config) with the ‘/uploads’.
define( 'UPLOADS', '/wp-content/uploads' );
Theres a catch though, when UPLOADS is actually used, its added to the end of ABSPATH. Defining ABSPATH one fo the first things WordPress does, its a constant which houses the WordPress root. By tying UPLOADS to ABSPATH, it means we can’t move the uploads directory outside of the WordPress folder structure.
As a result of this, I decided to write my first ever patch for the WordPress core. The initial patch creates WP_UPLOADS_DIR and WP_UPLOADS_URL, so that the uploads can be defined via wp-config.php just like wp-content and the plugins directory. It defines those constants in the same way also, in default-constants.php – I then simply modified wp_upload_dir() to use these constants rather than hardcoding a definition each time the function is run.
It seems to be a simple solution and would be a natural extension of the method for moving wp-content and plugins.
Reaction to my humble patch was not enthusiastic as you can see by reading the comments on the ticket #18489. After som protracted debate with Nacin and some others on the #wordpress-dev IRC channel, I identified what seems to be the primary concern – that developers might mistakenly begin to use the said constant within plugins instead of using wp_upload_dir() as they should, and that its availability would create confusion since in many cases it would not have the correct path (wp_upload_dir has many condition related to multisite as well as the font-end option that overwrite the default, and that would also overwrite the new constants.)
A possible solution…
As a result of that debate in IRC, I have modified the patch so that it does not define the constants by default, but stead works the same way the little-known UPLOADS constant does – simply allow wp_uploads_dir() to check if they have been defined – if they have, then it will use them, otherwise it will use use WP_CONTENT_DIR . ‘/uploads’;