Require Login to Access WordPress Media Files

Category: Blog • Posted by Jeff Starr • Updated:

This tutorial shows how to require user login to access any files stored in the WordPress Media Library. So for example, let's say that you have a bunch of images uploaded via the Media Library. And you want to protect those images, so that they are not accessible to the general public. The user must log in to access any media files. Once the user is logged in, then they will have full access to all files. This technique is useful for any site where you want to require login to access content.

Important!

The technique described in this article uses cookies to determine if the user should have access to uploaded/media files. So it works great for like 99.999% of users. A determined attacker, however, could bypass this method and access your media files. If you need something that is 100% bullet-proof and secure, check out the technique described in this post; it requires a bit more setup/configuration, but is well-tested and works great.

A little more context..

There are plugins that make it easy to transform any WordPress site into a site where users have to log in to access posts and other front-end content. Depending on which login-required type plugin you are using, your media files may not be protected. That is what this tutorial shows how to do, with a simple rule added to Apache/.htaccess.

WordPress Media Files

First, here is the default location for all files uploaded via the WordPress Media Library:

/wp-content/uploads/

So all of your media files will be located there. Or if you have enabled the WordPress Media setting "Organize my uploads into month- and year-based folders", then inside of the /uploads/ folder you'll have subfolders like this:

/wp-content/
	/uploads/
		/2021/
		     /01/
		     /02/
		     /03/
		     ...
		/2022/
		     /01/
		     /02/
		     /03/
		     ...
		/2023/
		     /01/
		     /02/
		     /03/
		     ...

You want to familiarize yourself with the contents of the /uploads/ directory. That is where your media files are located. And that is where we will be adding a simple slice of .htaccess.

Require login to access media files

First create a new .htaccess file inside of the /wp-content/uploads/ directory. Inside of the new .htaccess file, add the following code:

# require login for media files
# more info: https://m0n.co/11
<IfModule mod_rewrite.c>
	RewriteCond %{REQUEST_FILENAME} (.*)
	RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_([a-zA-Z0-9_]*) [NC]
	RewriteRule .* - [F,L]
</IfModule>

No editing is required. Once that code is in place, upload to the server and done. Of course, you should verify that everything is working by testing some of your media files. Try opening several random files while logged into WordPress. You should have normal access to all files. Then log out of WordPress and try again. You should not have access to any media files while logged out of WordPress.

How it works

How does the above code technique work? First notice the opening and closing <IfModule> containers. That checks for the required Apache module, mod_rewrite, before attempting to execute any contained rules.

Inside of the <IfModule> containers, there are three rules that do the following (in order):

  1. Check if the request is for any file
  2. Check for the absence of a cookie that begins with wordpress_logged_in_
  3. If these conditions are met, the file request will be denied via 403 "Forbidden" response

The trick here is step 2, the check for the absence of a cookie that begins with wordpress_logged_in_. When the user is logged in, WordPress adds a cookie to your browser that looks like:

wordpress_logged_in_1234567890abcdefghijklmnopqrstuvwxyz

The appended alphanumerical string can be any sequence of (case-insensitive) letters and numbers. If such a cookie is not found, then the user is not logged in to WordPress, and thus Apache will return a 403 Forbidden response.

Pro Tip: To dive further into Apache/.htaccess and mod_rewrite, check out my book .htaccess made easy.

Tips and Tricks

Let's say that, for whatever reason, you want to require login only for specific types of media files. For example, if you are selling access to PDF files, or making them available to members only. In such case, you want to make sure the user is logged in before they can access/download any of the PDF files. Here is the code to make it happen. Instead of the above code snippet, add this instead:

# require login for PDF files
# more info: https://m0n.co/11
<IfModule mod_rewrite.c>
	RewriteCond %{REQUEST_FILENAME} \.(pdf)$ [NC]
	RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_([a-zA-Z0-9_]*) [NC]
	RewriteRule .* - [F,L]
</IfModule>

Notice the difference between the first code technique and this one. Here we are checking if the requested file name ends specifically with .pdf (case-insensitive), which indicates a PDF file. So if the user is not logged in, they cannot access/view any PDF files. But they will be able to access/view all other types of media files. For example, logged-out visitors will be able to view any images that you've included in your post content.

Multiple file types

While we're here, let's take it a bit further and say that we have multiple file types that should require user login. Easy. Just change the second line (the first RewriteCond) to this:

RewriteCond %{REQUEST_FILENAME} \.(pdf|zip|mp4|ogv|webm)$ [NC]

So you can guess which files we are matching here: PDF, ZIP, MP4, OGV, and WEBM, all case-insensitive. Likewise you can edit the file types to whatever is required.

Redirect to WP Login Page

All of the above techniques simply return a 403 Forbidden response. But depending on which file(s) you are restricting, it may also make sense to redirect the user to the WordPress Login Page. You know, like if some user tries to access a PDF, they are redirected to the login page. Or you can redirect to any URL, or return an image, or do just about anything.

In any case, to redirect the user to the login page (instead of sending 403 error), replace the RewriteRule in any of the above code techniques with this:

RewriteRule (.*) https://example.com/wp-login.php [R=301,L]

Two things to change here:

  • The redirect URL — Change the https://example.com/wp-login.php to match the URL of your actual login page (or any URL that you want).
  • The response code — Instead of returning a status of 301 "Permanent Redirect", you may decide to return a different response, like 302 "Temporary Redirect", or whatever makes sense for your setup.

Pro Tip: To go even further with redirecting and optimizing traffic, check out my book .htaccess made easy.

Related Resources

Here are some related articles that may be useful.