Edit: I've received some valuable feedback from Reddit users: cabalos & zware. It seems the following approach may be bypassed by an experienced developer. Will come up with an alternative solution and update this post again.

!!Edit2:!! For a far more effective protection check the free plugin below. It uses the technique mentioned in this post and also combines it with a system WordPress plugin to do more thorough checks. Below is the 2nd video which demonstrates the more effective approach in protecting WP Media Uploads folder.

Orbisius WP Media Protector

When you upload files in WordPress by default they go to wp-content/uploads folder.

Those files are normally accessible from the browser and it is supposed to be that way because the transfer is faster. Serving files from php, perl etc would take additional resources.

What if you have project that requires all files to be accessible to the logged in users only?

For example this could be an internal site that allows employees, vendors can upload documents. The admin creates the accounts on demand and it’s tightly controlled who gets access to the site.

Here is a video demo how to do it.

 

Here some snippets for Apache web server that need to be added to the .htaccess file which resides in the folder where WordPress is installed.

Option 1: Full Restriction: How to restrict access to all files from residing in the WordPress uploads folder.

# Protect all files within the uploads folder
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{HTTP_COOKIE} !.*wordpress_logged_in.*$ [NC]
	RewriteCond %{REQUEST_URI} ^(.*?/?)wp-content/uploads/.* [NC]
	RewriteRule . http://%{HTTP_HOST}%1/wp-login.php?redirect_to=%{REQUEST_URI} [L,QSA]
</IfModule>

Option 2: Partial Restriction: How to restrict access to only to some files (based on their extension) in the uploads folder.

# Protect only some files within the uploads folder
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteCond %{HTTP_COOKIE} !.*wordpress_logged_in.*$ [NC]
	RewriteCond %{REQUEST_URI} ^(.*?/?)wp-content/uploads/.*\.(?:gif|png|jpe?g|pdf|txt|rtf|html|htm|xlsx?|docx?|mp3|mp4|mov)$ [NC]
	RewriteRule . http://%{HTTP_HOST}%1/wp-login.php?redirect_to=%{REQUEST_URI} [L,QSA]
</IfModule>

 

Note: This works for Apache web server and can be tweaked to work with nginx as well.

 

How it works?

Apache mod_rewrite module checks to see if there’s a cookie whose name contains  “wordpress_logged_in”. If it doesn’t exist that means that the user is not logged in. Next rule checks if the user is trying to access a file in the uploads folder.

Based on the which option you’ve picked the rules check if it’s any file is accessed from within uploads OR a file with a known extension.

The last line redirects the user to the login page and if they successfully login then they’ll be redirected to the file they have tried to access. Other solutions just redirect to random locations or output an access denied error message.

Note: WordPress’ uses a php constant called LOGGED_IN_COOKIE which contains the full name of the cookie.

Related