I’ve recently had a number of requests for custom NGINX rules to be created in order to protect WordPress upload directories. This scenario may surface if you are using NGINX as your primary web server or NGINX sits before say an upstream Apache server and the .htaccess files is not an option. I have recently configured several rules to protect directories.
The following rule is specifically for static files, such as PDFs:
if ( $uri ~* ^/wp-content/uploads/.+\.pdf$ ) {
set $var 1;
}
if ( $http_cookie !~* wordpress_logged_in ) {
set $var "${var}1";
}
if ( $var = 11 ) {
return 403;
}
The above rule works on by checking if the incoming request URI is looking for PDF files within /wp-content/uploads/ and if so whether the wordpress_logged_in cookie is set. If it is not then return the a HTTP 403 response. Since NGINX does not use if/else statements, we build out our rule using several if statements.
Another example we can use is the following, which uses a location block to achieve similar:
location ~* ^/wp-content/uploads/protected/(?!public_images/).+\.jpg$ {
add_header Cache-Control "no-store; max-age=0";
if ( $http_cookie !~* wordpress_logged_in ) {
return 403;
}
}
The above again uses a very similar process, however this time using a location block. The reason behind using a location block is since we are handling JPG files in this examples, we do not want additional caching layers to cache these when a logged in user views them. Since we want to alter the behavior of the JPG files in this specification path only, to remove pre-existing configurations, we carve out a location block and pass back the Cache-Control header. By setting the no-store argument, it tells caching applications not to store the response on cache. By reducing the max-age to zero, we also force browsers to check the header each time.
As an added feature, in this example I have implemented a negative lookahead in order to ignore images found in the public_images directory, which we want users who have not logged in to be able to access.