Using “wordpress_logged_in” to restrict direct access to uploads folder in 2021

Check to see if a person is logged in:

<IfModule mod_rewrite.c>
    RewriteCond %{REQUEST_FILENAME} (.*)
    RewriteCond %{HTTP_COOKIE} !wordpress_logged_in_([a-zA-Z0-9_]*) [NC]
    RewriteRule .* - [F,L]
</IfModule>

The same applies as it did 10 years ago. This code in .htaccess doesn’t have anything to do with how WordPress might be setting cookies. And this does not “check to see if a person is logged in”. It simply checks that a string that matches the regex wordpress_logged_in_([a-zA-Z0-9_]*) does not appear anywhere in the Cookie header. The “problem” here is that anyone can create a request that passes a value in the Cookie header that passes your test. Since it is well known that WP sets such a cookie then it is not difficult to guess.

The only way to check that the request is “logged in” to WordPress is to use PHP.


Aside: The first RewriteCond directive that checks against REQUEST_FILENAME is superfluous since it simply checks against anything. The NC flag on the second condition should not be used in such checks, since you are now unnecessarily increasing the range of strings that would pass the test (eg. wOrDpReSs_LoGgEd_In_ would pass). Consequently, including A-Z in the hash portion is unnecessarily – the hash is always lowercase (and always 32 chars long AFAIK). The L flag is not required when used with the F flag (it is implied) and the regex .* will block all requests if that cookie does not exist, not just access to the WordPress uploads folder. You should also avoid using the <IfModule mod_rewrite.c> wrapper for anything security related, since a “failure” in your server config (ie. mod_rewrite being erroneously disabled) would allow unrestricted access, instead of breaking with an error (which would be preferable).

So, the above should be written like this:

RewriteCond %{HTTP_COOKIE} !(?:^|;\s)wordpress_logged_in_[a-z0-9_]{32}=
RewriteRule ^wp-content/uploads/ - [F]

The regex (?:^|;\s)wordpress_logged_in_[a-z0-9_]{32}= specifically checks for that cookie name (more specific), rather than a string that occurs anywhere in the Cookie header (too general).

(Although, to clarify, this is really no more “secure” than the original code snippet.)