Wordpress appears to let content be addressed by many URIs in an MU install. For example:


All appear to route to the same place. Are there more aliases? Which one is canonical? Are any of these accidental? Are there relevant filters?


Short Answer

example.com/bob/files/picture.jpg is the preferred, canonical URL for images in a WordPress Multisite installation. The two URLs with blogs.dir in the URL are essentially identical, and both leverage the filesystem structure. The path with 'bob' exists because you did a sub-directory install, not a subdomain install. Other paths would exist based on your other sites, e.g. example.com/fred/wp-content/blogs.dir/5/files/pictures.jpg. Otherwise, no other paths exist.

Long Answer

There's a lot one can explain about this process, and I'm not 100% certain of the level of detail you're looking for, so I'll cove the basics here.

WordPress Multisite stores files by blog_id (the "5" after "/blogs.dir/") to keep things organized and separate different sites' files. This directory structure isn't intended to be public. WordPress uses rewrite rules to route ^files/(.+) to wp-includes/ms-files.php?file=$1, and then wp-includes/ms-files.php processes and outputs the image and/or some helpful headers. There are a few advantages to this:

  • In terms of security, less information is always better. "wp-content/blogs.dir/5" is a little TMI -- it says you're running WordPress Multisite and that the blog_id is 5.
  • The URL structure is identical to that of a single-site installation. If you were to ever move a site from a Multisite installation to its own, you wouldn't have to update those references in the database or 301 the old paths for external references.
  • You can move the files directory out of public access or deny from all through .htaccess so, for instance, people can't access original image sizes if you don't want.
  • You can add access control to specific files

There's one main disadvantage, which is that the images/files are passing through PHP (and perhaps even require a few MySQL queries), so it requires more resources. If you have a caching plugin installed, the additional resources should be negligible.

With regards to filters, you can't easily filter anything in the process for one reason: neither mu-plugins, plugins, or your theme get loaded*. The best you can do is override constants in wp-config.php. Here are the most useful/relevant constants you can override:

if ( !defined( 'UPLOADBLOGSDIR' ) )
    define( 'UPLOADBLOGSDIR', 'wp-content/blogs.dir' );

if ( !defined( 'UPLOADS' ) ) {
    // Uploads dir relative to ABSPATH
    define( 'UPLOADS', UPLOADBLOGSDIR . "/{$wpdb->blogid}/files/" );
    if ( 'wp-content/blogs.dir' == UPLOADBLOGSDIR )
        define( 'BLOGUPLOADDIR', WP_CONTENT_DIR . "/blogs.dir/{$wpdb->blogid}/files/" );

 * Optional support for X-Sendfile header
if ( !defined( 'WPMU_SENDFILE' ) )
    define( 'WPMU_SENDFILE', false );

 * Optional support for X-Accel-Redirect header
if ( !defined( 'WPMU_ACCEL_REDIRECT' ) )
    define( 'WPMU_ACCEL_REDIRECT', false );

*Even though plugins don't get loaded, drop-ins do. Therefore, while you can't use standard plugins, WordPress still lays the foundation to do whatever you need, like (as mentioned above) adding access control to sensitive files. The drop-in sunrise.php would be a good place to add such code.


In multisite installs setup with WordPress 3.5 or later, ms-files.php is bypassed directly, and blogs.dir is not used.

Instead, uploads are stored in wp-content/blogs/{blog_id} and referred to as such. This means that there is only one way to access files on a subdomain install, and only two on subdirectory install.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.