The .htaccess
file is a configuration file used by the Apache webserver to customize the server’s behavior. WordPress uses this file to manage its URL rewriting rules. Below is a code block that modifies the default WordPress rules to allow for the loading of images from a production site instead of the local development site.
Loading images from production instead of development can be useful in scenarios where you have a large number of media files on the production site and don’t want to download them to the development environment. This approach can help speed up the development process and reduce the amount of storage space required for the development site. Additionally, it can help ensure that the site’s design and layout look consistent with the production site since the same images are being used.
There may be endless reasons why would you like to do it, but let’s get to code.
Solution
In the .htaccess
file, WordPress adds a small code block by default, which starts with # BEGIN WordPress
and ends accordingly with # END WordPress
.
Now, before this block, we need to insert the code lines that will rewrite the image URLs. It’ll basically show the normal image URLs, but will actually load them from production or a CDN, you name it…
Find the line that says # BEGIN WordPress
and exactly before it, insert this code block by replacing example.com
with your prod server address:
<IfModule mod_rewrite.c>
# If images not found on development site, load from production
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^wp-content/uploads/[^/]+/.+\.(jpe?g|png|gif)$ https://example.com/$0 [R=302,L]
</IfModule>
If you want to get into technical details here’s how does it work:
This code checks if the requested file is not found on the local development site (RewriteCond %{REQUEST_FILENAME} !-f).
If it’s an image file in the wp-content/uploads/ directory (RewriteRule ^wp-content/uploads/[^/]+/.+.(jpe?g|png|gif)$), then the rule redirects the request to the corresponding URL on the production site (https://example.com/$0).
The $0 variable represents the entire matched string from the RewriteRule pattern.
The [R=302,L] flags indicate that it’s a temporary redirect (302) and that no further rules should be processed (L).
Example of a full code block
Please note that the code within the WordPress begin and end tags may differ on your site. Therefore, rather than copying and pasting the code below, use it as an example of how I implemented it on my site and modify it to suit your needs.
<IfModule mod_rewrite.c>
# If images not found on development site, load from production
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^wp-content/uploads/[^/]+/.+\.(jpe?g|png|gif)$ https://example.com/$0 [R=302,L]
</IfModule>
# BEGIN WordPress
RewriteEngine On
RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
# END WordPress
Make a must-use plugin
These rules will be overridden sometimes by plugins, wp core updates, and so on. To prevent this, we can create a mu-plugin, that will prepend this code each time is needed.
In WordPress, we have insert_with_markers
function, which is usually used to write in .htaccess file, but it does not allow to insert before other rules. It’ll just insert our code at the end of the file. We can fix this by creating a custom function. Here is the full code of a mu-plugin like this:
<?php
/*
Plugin Name: Prod to Dev Images Mirror
Description: Inserts custom rewrite rules into .htaccess
*/
function zerowp_prod_to_dev_images_mirror() {
$marker = 'Prod to Dev Images Mirror';
$insertion = array(
'<IfModule mod_rewrite.c>',
'# If images not found on development site, load from production',
'RewriteCond %{REQUEST_FILENAME} !-f',
'RewriteRule ^wp-content/uploads/[^/]+/.+\.(jpe?g|png|gif)$ https://example.com/$0 [R=302,L]',
'</IfModule>'
);
$filename = ABSPATH . '.htaccess';
// check if file is writable
if (!is_writable($filename)) {
return false;
}
$lines = explode("\n", file_get_contents($filename));
$new_file = '';
$found = false;
$already_present = false;
foreach($lines as $line_number => $line) {
if (strpos($line, $marker) !== false) {
$already_present = true;
break;
}
if (! $found && strpos($line, '# BEGIN WordPress') !== false) {
$new_file .= "# BEGIN {$marker}\n";
$new_file .= implode("\n", $insertion);
$new_file .= "\n# END {$marker}\n";
$found = true;
}
$new_file .= $line . "\n";
}
if ($already_present) {
return true;
}
// Insert at top if # BEGIN WordPress is not found
if (! $found) {
$insertion = "# BEGIN {$marker}\n" . implode("\n", $insertion) . "\n# END {$marker}\n";
$new_file = $insertion . $new_file;
}
// Write the changes to the file
return file_put_contents($filename, $new_file);
}
add_action('admin_init', 'zerowp_prod_to_dev_images_mirror', -100);
The function above can be modified and used to achieve other tasks, and you’ll have your own insert_with_markers function that is able to prepend the code.
Again, don’t forget to replace example.com
with your domain.
Conclusion
Overall, this code block modifies the default WordPress URL rewriting rules to allow for the loading of images from a production site when they are not found on the local development site. This can be useful in scenarios where developers want to test their site’s functionality without having to download all the media files from the production site.