Membership Plugin

WordPress Membership Plugin

  • Home
  • Documentation
  • Addons
  • Support
    • Quick Setup
    • Documentation
    • Premium Addon Support
    • Paid Support
    • Support Forum
    • Support Forum Search
    • Forum Login
    • Forum Registration
  • Contact

[Resolved] Protecting Files/Media

Simple Membership Plugin › Forums › Simple Membership Plugin › Protecting Files/Media

Tagged: nginx

  • This topic has 9 replies, 5 voices, and was last updated 1 year ago by admin.
Viewing 10 posts - 1 through 10 (of 10 total)
  • Author
    Posts
  • November 2, 2021 at 1:48 am #23225
    Eldon McGuinness
    Participant

    I wanted to protect certain posts, pages, and files/media and decided to give it a try using categories. To that end, here is how I am able to achieve it. This is based on the idea from hakre @ stackexchange

    First, you’re going to need some way to redirect downloads to a PHP file that can check authorization and either direct the user to login OR prompt the download to happen.

    .htaccess [Root of WordPress site folder]

    
    RewriteCond %{REQUEST_FILENAME} -s
    RewriteRule ^wp-content/uploads/(.*)$ dl-file.php?file=$1 [QSA,L]
    

    dl-file.php [Root of WordPress site folder]

    
    <?php
    
    // Using Simple Membership Plugin to verify the user has access to the file
    function can_access_attachment($id) {
    
    	$auth = SwpmAuth::get_instance();
    	$protected = SwpmProtection::get_instance();
    
    	//Check if this is a protected post.
    	if (!$protected->is_protected($id)) {
    		return true;
    	}
    
    	//Check if the user is logged in.
    	if(!$auth->is_logged_in()){
    		return false;			
    	}
    
    	//Check if the account is expired
    	if ($auth->is_expired_account()){
    		return false;						
    	}
    	
    	//Check if this user's membership level has access to this post
    	$permission = SwpmPermission::get_instance($auth->get('membership_level'));
    	if(!$permission->is_permitted($id)) {
    		return false;
    	}
    		
    	//All checks have passed. Show this post to the user.
    	return true;	
    }
    
    require_once('wp-load.php');
    
    if ( !is_user_logged_in() || !SwpmMemberUtils::is_member_logged_in() ){
    	auth_redirect();
    }
    
    list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);
    list($baseurl) = array_values(array_intersect_key(wp_upload_dir(), array('baseurl' => 1)))+array(NULL);
    
    $filename = basename($_GET['file']); 
    $file_path = rtrim($basedir,'/').'/'.str_replace('..', '', isset($_GET[ 'file' ])?$_GET[ 'file' ]:'');
    $url  = rtrim($baseurl,'/').'/'.str_replace('..', '', isset($_GET[ 'file' ])?$_GET[ 'file' ]:'');
    $id = attachment_url_to_postid( $url );
    
    if (can_access_attachment( $id )) {
    	
    	if (!$baseurl || !$basedir || !is_file($file_path)) {
    		status_header(404);
    		die('404 — File not found.');
    	}
    	
    	$mime = wp_check_filetype($file_path);
    	if( false === $mime[ 'type' ] && function_exists( 'mime_content_type' ) )
    		$mime[ 'type' ] = mime_content_type( $file_path );
    	
    	if( $mime[ 'type' ] )
    		$mimetype = $mime[ 'type' ];
    	else
    		$mimetype = 'image/' . substr( $file_path, strrpos( $file_path, '.' ) + 1 );
    
    	header( 'Content-Type: ' . $mimetype ); // always send this
    
    	if ( false === strpos( $_SERVER['SERVER_SOFTWARE'], 'Microsoft-IIS' ) )
    		header( 'Content-Length: ' . filesize( $file_path ) );
    
    	$last_modified = gmdate( 'D, d M Y H:i:s', filemtime( $file_path ) );
    	$etag = '"' . md5( $last_modified ) . '"';
    	header( "Content-Disposition: attachment; filename=$filename" );
    	header( "Last-Modified: $last_modified GMT" );
    	header( 'ETag: ' . $etag );
    	header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time() + 100000000 ) . ' GMT' );
    
    	// Support for Conditional GET
    	$client_etag = isset( $_SERVER['HTTP_IF_NONE_MATCH'] ) ? stripslashes( $_SERVER['HTTP_IF_NONE_MATCH'] ) : false;
    
    	if( ! isset( $_SERVER['HTTP_IF_MODIFIED_SINCE'] ) )
    		$_SERVER['HTTP_IF_MODIFIED_SINCE'] = false;
    
    	$client_last_modified = trim( $_SERVER['HTTP_IF_MODIFIED_SINCE'] );
    	// If string is empty, return 0. If not, attempt to parse into a timestamp
    	$client_modified_timestamp = $client_last_modified ? strtotime( $client_last_modified ) : 0;
    
    	// Make a timestamp for our most recent modification...
    	$modified_timestamp = strtotime($last_modified);
    
    	if ( ( $client_last_modified && $client_etag )
    		? ( ( $client_modified_timestamp >= $modified_timestamp) && ( $client_etag == $etag ) )
    		: ( ( $client_modified_timestamp >= $modified_timestamp) || ( $client_etag == $etag ) )
    		) {
    		status_header( 304 );
    		exit;
    	}
    
    	// If we made it this far, just serve the file
    	readfile( $file_path );
    
    }else{
    	auth_redirect();
    }
    
    ?>
    

    functions.php

    
    <?php
    defined('ABSPATH') or die("Please don't run this script.");
    
    function pages_and_media_with_category_and_tag_register(){
    	/*add categories and tags to pages*/
    	register_taxonomy_for_object_type('category', 'page');
    	register_taxonomy_for_object_type('post_tag', 'page');
    	register_taxonomy_for_object_type('category', 'attachment');
    	register_taxonomy_for_object_type('post_tag', 'attachment');
    }
    add_action( 'init', 'pages_and_media_with_category_and_tag_register');
    
    function pages_and_media_with_category_and_tag_register_pre_get( $query ) {
    
    	if ( is_admin() || ! $query->is_main_query() ) {
    		return;
    	}
    
    	/*view categories and tags archive pages */
    	if($query->is_category && $query->is_main_query()){
    		$query->set('post_type', array( 'post', 'page', 'attachment'));
    	}
    
    	if($query->is_tag && $query->is_main_query()){
    		$query->set('post_type', array( 'post', 'page', 'attachment'));
    	}
    }
    add_action( 'pre_get_posts', 'pages_and_media_with_category_and_tag_register_pre_get' );
    

    The functions.php edit allows you to have categories for pages and media as well as posts.

    With this setup you can set categories on all three and protect files from external linking. Let me know if you find any issues with this or bugs.

    November 3, 2021 at 4:16 am #23233
    mbrsolution
    Moderator

    Hi, thank you for sharing your solution. I am sure others will find your solution helpful.

    Kind regards.

    November 3, 2021 at 8:54 am #23239
    admin
    Keymaster

    Thank you. I will add a reference to this on our following page:
    https://simple-membership-plugin.com/user-submitted-tweaks-simple-membership/

    December 4, 2021 at 11:46 pm #23340
    Dan
    Participant

    Has anyone had any luck with getting this to work on nginx? Running nginx and php8.0 I change the nginx config to match the rewrite rule for the htaccess file
    I tried

    
    location / {
      if (-e $request_filename){
        rewrite ^/wp-content/uploads/(.*)$ /dl-file.php?file=$1 last;
      }
    }
    

    and

    
    location ~* /(?:uploads|files)/* {
        rewrite /wp-content/uploads/(.*)$ /dl-file.php?file=$1 last;
    }
    

    And only get dl-file.php downloaded when trying to access the media file as if the FastCGI server isn’t running, however it is and WordPress itself isn’t having any issues.

    December 5, 2021 at 1:58 am #23341
    Dan
    Participant

    So in my case for nginx I wrote this for my site config. This will block someone from having direct access to the file and they cannot download it, and they will only be able to access it if the website serves them the post (so if it’s a member only post, only members can see the file). For now this is the functionality I am looking for, and it only needs the config to be edited without the need for additional scripts.

    
    	# Hotlink protection
    	location ~ ^/wp-content/uploads/(.*) 
    	{
    		valid_referers YOURSITEHERE.com www.YOURSITEHERE.com;
    		if ($invalid_referer) 
    		{
    			return 403;
    		}
    	}
    

    Hope someone else finds it useful

    December 5, 2021 at 6:51 am #23343
    Eldon McGuinness
    Participant

    Has anyone had any luck with getting this to work on nginx?
    Yes, I wrote the original using NGINX, I would need to check and see what version of PHP, but I think it was 7.x. Though, that should not matter.

    	# Hotlink protection
    	location ~ ^/wp-content/uploads/(.*) 
    	{
    		valid_referers YOURSITEHERE.com www.YOURSITEHERE.com;
    		if ($invalid_referer) 
    		{
    			return 403;
    		}
    	}

    This would only prevent them from “hot-linking” to the file, it would not prevent someone from inserting a link into the page or faking the headers and then downloading the file. This really is not a good solution for protecting files. Glad it works for you, but you might want to consider something more robust.

    December 13, 2021 at 2:47 am #23379
    Dan
    Participant

    After a server restart it seems to be working fine now with PHP8.1
    Thanks!
    I am however not able to preview the file in the media library when logged in as admin, though I think that has more to do with this plugin as I am not able to see the posts as well, only users with the proper membership tag are bale to see, but admins are not. I’ll play with the settings.

    December 13, 2021 at 3:55 am #23380
    mbrsolution
    Moderator

    @Dan, please check the following documentation if you want to give admin access to protected posts and pages.

    https://simple-membership-plugin.com/giving-wordpress-admin-user-access-protected-content/

    Thank you.

    March 28, 2022 at 10:14 pm #23755
    Faraz
    Participant

    I tried implementing this solution, but it made it so that every item in the media library failed to load, except for any item I explicitly defined content restrictions for. Any file that did not have protection assigned failed to load at all. So, a PDF that I defined content restrictions for, would show up restricted for non-members, and download correctly for members. But anything that had no protection on it, would fail to load for non-members. If you were logged in the site loaded normally. Any way to make it load normally for non-members?

    May 10, 2025 at 3:22 am #30370
    admin
    Keymaster

    Adding to this topic, we now also have the following integration that can be used to protect media files:

    https://simple-membership-plugin.com/protecting-media-files-in-wordpress-static-file-security-and-access-control/

  • Author
    Posts
Viewing 10 posts - 1 through 10 (of 10 total)
  • You must be logged in to reply to this topic.
Log In

Please read this message before using our plugin.

Search

Featured Addons and Extensions

  • Membership Form Builder Addon
  • Member Directory Listing Addon
  • WooCommerce Payment Integration
  • Member Data Exporter Addon

Documentation

  • Documentation Index Page

Copyright © 2026 | Simple Membership Plugin | Privacy Policy