Moving the Uploads Directory in WordPress

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…

Moving wp-content

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.

A patch…

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.

The debate…

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’;

Advertisements

19 Comments on “Moving the Uploads Directory in WordPress”

  1. […] Si queréis más información sobre el tema (en inglés), podéis leer la entrada en el blog de Eddie Moya y el ticket 21720 de […]

  2. I have my WordPress installation in a folder on the server (eg: /wordpress).
    I would like to set the upload folder outside of it (eg: /my-upload-folder instead of /wordpress/wp-content/uploads).

    There is any trick to do it with WordPress 3.5.1?
    I tried with define(‘UPLOADS’, ‘../my-upload-folder) and it works in some way.. I mean, the images are inside /my-upload-folder but WordPress create a copy of the subfolder in /wordpress/wp-content/uploads anyway). Moreover, in the media library the images are flagged with an URL like “http://www.myserver.com/wordpress/../my-upload-folder/myimage.jpg” which is totally wrong.

    Any suggestion please? 🙂

    Thank you very much!

    • eddiemoya says:

      There is no clean way to move the uploads folder outside of the main WordPress folder through configs. The UPLOADS definition is appended to ABSPATH, there is no way to get around this. There are however a couple of workarounds that either require

      #1. I believe you should be able to change it manually through the settings page for Media. However this is a setting that you’ll have to set in each environment – it gets stored in the options table, which may be undesirable.
      #2. Uploads folder will come along with wp-content if you move it. So if you move wp-content to somewhere outside wordpress, uploads will follow, but you cant target uploads specifically and have it be outside wordpress.
      #3. Symlinks + Apache aliases (or Nginx “locations”) can be used to put images wherever you want. However its not a self-contained wordpress solution and is entirely dependent on access to configure your environment.

      • Well, this thing is quite annoying. I can’t figure out why they didn’t add the support for a constant like WP_UPLOADS_DIR as you suggested, that would be so easy and I think not so hard to implement.

        Anyway, I tried to maintain that UPLOADS constant (because, after some tests, I discovered that all the images are effectively stored into /my-upload-folder). Then I try to use a filter for upload_dir function but I didn’t find a solution (for example, I tried to str_replace the various variables to delete the ../ chars, but in this way the image url was completely wrong), so there’s no way to do it in this way, I suppose.

        I’m not so into server knowledge to understand your point #3, but what do you think about maintain the wp-content/uploads where it is and use a sort of rewrite url? I saw that http://www.rootstheme.com/ use it, but I have to still understand how does it work. It would be a pain in the ass for the server? It’s for an online magazine, so there will be a lot of image requests.

      • eddiemoya says:

        WordPress rewrite rules will not work. Two things need to happen, WordPress needs to know where its supposed to save the images when they are uploaded through the admin and you server needs to know where to find them when a browser requests them. WordPress rewrite rules have nothing to do with either of these things. Using .htaccess rewrites might get you to the right path for requests from the browser, but WordPress is still going to be saving images in the wrong location.

        Your best strategy for moving the uploads folder outside of the root WordPress directory is going to be setting up a symlink, and then telling the server where to go to find images (kind of like a rewrite rule in a way).

        The symlinks are simple enough, just do a search on creating symlinks – it will make it so that /wp-content/uploads actually points to a different directory – like shortcuts in Windows. If I can assume you are using apache, then all you need to do is set up an alias. I would suggest doing a small amount of reading regarding how this is done. Aliases are pretty simple also, its just editing one file in the server configs. If you are using nginx, you need to setup a custom “location”, which is equally simple.

      • Well, after some testing, I discovered that updating “upload_path” and “upload_url_path” fields into the “wp_options” table, everything works fine. To be precise, in the “upload_path” I wrote the complete path on server of the folder (even outside WordPress) – for example: /www/home/my-upload-folder – and the URL in “upload_url_path” – such as: http://www.mysite.com/my-upload-folder

        I know that this means that WordPress has to retrieve the correct folder in the database each time I upload a media file, but I found this method to be the most easy to implement (until the WP_UPLOADS_DIR will not become reality).

        Thank you anyway for your support! 🙂

      • eddiemoya says:

        Yes, the wp_options method is what I was referring to in method #1. That settings field sets the wp_options value. Although now that I think about it – I think they might have removed that settings field from the admin area.

        Be careful with using this method. This makes your database less portable, and it makes the process of deploying these types of setups more difficult to automate.

  3. ESSAM says:

    One of the best posts about moving wp-content & uploads folder. Thanks!

    – I was wondering if I host several sites/domains that all share the same plugins and theme folder from one location, would that put a load on the server? will it send too many request?

    • eddiemoya says:

      Assuming that all those sites/domains are on the same machine, as far as I know it should be no different than having them run with separate directories. The traffic of all the sties combined shouldn’t have any negative effects just because they are all hitting the same specific files.

      However if your doing this though an NFS mount, then that changes things because the NFS mount may have a limit of concurrent connections. If the NFS mount is actually on different machine(s) then it will also need to travel over a physical network which may have different limitations or different thresholds for how much traffic it can handle.

  4. a year and a half and this still hasn’t made it into core. #smh

  5. would love for this to be implemented as I have configured my local dev with one wordpress install. want all my ‘sites’ to share the same wp-content/themes|plugins directories but have their separate uploads dir.

  6. shawn says:

    Thank you for the article. I am using nginx and have the symlink setup, but seem to be stuck on the location portion for nginx.

    My setup is fairly simple:
    site is in /home/mysite.com/
    I used an symlink… ln -s /home/uploads uploads
    That way I have an uploads ‘symlink’ in my wordpress install /home/mysite.com/uploads
    I then defined the uploads in my wp-config.php
    define(‘UPLOADS’, ‘/uploads’);

    So basically the problem I have now is that it can’t create the year folders asking if the directory is writable or not.

    My best guess is that when WordPress tries to save the file, it somehow knows it is trying to save to a symlinked location and errors out with a permission issue.

    I did set my symlink directory to 0755 ‘+x’ and made sure that the symlink dir is owned by www-data which is appropriate for my server.

    So, it must come down to the nginx location you mentioned.

    Any ideas on how to help with the nginx location?

    thank you again for the article, looks like I’m 99% of the way there because of it.

    • eddiemoya says:

      Sorry that I never noticed your question.
      This sounds like a permissions issue with nginx/php(fpm?). Pretty separate from what this article talks about. However, if I remember correctly, you need to make sure the owner of the directory is the same as the user running nginx, and (i think) for WordPress to create those folders, you also need you php(-fpm) user to be the same user as well.

      Hope that helps. Sorry for not responding sooner.

  7. cob-web says:

    @Pietro Gregorini
    Just found this plugin which does the same job:
    http://wordpress.org/plugins/upload-url-path-enabler/

    It helps me to synchronise the uploads folder on my testsite without having to edit the database 🙂

    • eddiemoya says:

      Thanks for pointing this out. Thats useful but it still doesn’t solve the problem of needing an operational way to set the uploads url outside of wp-content. When I wrote this article, that field was available. Its good that its there (because theres nothing else) but we need a way to set this in a deployable environmental way – without having to fully load the application, log in, and set a value in the database.

      It needs to be configurable in wp-config.

  8. edwardbeckett says:

    Try simply redefining the constant in wp-config.php

    /**
    * Move and rename the uploads folder…
    * you don’t need to prepend or append a directory slash
    */

    define( ‘UPLOADS’, ‘media’);

    • eddiemoya says:

      The only problem with the UPLOADS constant is that its not an absolute path, its relative to ABS_PATH – which is the absolute path of your WordPress root. That means that if you use UPLOADS, you can’t move it outside of the WordPress folder the way you can when you move WP_CONTENT.

      That said, if thats what you want, or if you don’t mind the uploads directory sitting inside WordPress root, then using UPLOADS is just fine.

      Thanks for commenting!

  9. Nadav says:

    I wanted to thank you for creating this important post and also a big thanks to Pietro Gregorini for spelling out exactly how to facilitate the database change. Thanks to you two I now have my WordPress directories and images directories completely separate. You guys are true gentleman!


Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s