Serve WordPress images from a different domain with .htaccess

The Situation

So you’ve got a local version of WordPress humming along, let’s say at http://example.dev, but you also have a staging or production server running somewhere publicly accessible, let’s say http://example.com.

While it’s relatively quick and easy to keep the databases in sync using a tool like WP Sync DB, it can be more difficult and more time consuming to keep media files in sync. It can also take up a ton of your computer’s disk space to have an entire website’s uploads folder locally.

So is there a better way to handle this?

The Solution

Yes there is — .htaccess RewriteRules to the rescue!

This assumes that you’re hosting WordPress on an apache server. If you’re not sure if your WordPress server is using apache or not, there’s a good chance it is. I’m sure there are similar solutions out there for IIS and nginx, but I won’t be covering those here.

If you’re not familiar with it, the .htaccess file provides instructions to the server. You can do a ton of things with it, but one thing that’s really handy is being able to redirect traffic based on certain rules. These are called RewriteRules. WordPress actually uses this itself to route all requests to the primary index.php file, but I digress.

If you want example.dev to serve all of its images from example.com instead, just add this into your .htaccess file, above or below the WordPress block.

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^(wp-content/uploads/\d+/.*)$ http://example.com/$1 [R=301,NC,L]
</IfModule>

The Explanation

Some people may be happy just to have the snippet above and have it work, but if you’re reading this section, then you’re curious about how it works. I won’t get into all of the details, but I’ll talk about some:

The RewriteRule itself uses regular expressions to match certain patterns and then redirect that request to the new location (the target), which is specified right after the pattern.

The ^ symbol means the request (which is everything that comes after example.dev/) pattern has to start with what comes right after it.

The $ symbol represents the end of the pattern. Now that I think about it you might not even need it here.

The \d+ part of the pattern represents any digit. It’s intended to capture uploads folders like /2016 or /2015, assuming you have WordPress set up to create year based uploads folders. Some plugins, including WP Sync DB, create their own folders inside the uploads directory, so it could cause issues to leave this part off, but that’s up to you.

Using parentheses allows you to “capture” the actual value that matches the pattern inside them and then re-use it later. This is called a match group. We are re-using it in the target using $1.

An Example

The explanation above can be kind of hard to think about in the abstract, so let’s look at an example.

Let’s say you make a request to your local WordPress site, and WordPress wants to serve an image it knows should be at example.dev/wp-content/uploads/2016/image.jpg.

The request goes to apache, and apache checks the request against the .htaccess rule. It matches the pattern, because it starts with wp-content/uploads, then it has a number, then something, anything, comes after that. So it knows it should serve the image from http://www.example.com/wp-content/uploads/2016/image.jpg instead.

Magic!

If you stumbled across this, I hope you find it useful. Please let me know in the comments if it worked for you or if you have any questions!

Leave a Comment