How to Make your WordPress site for Members Only

Updated 12/2014

I was putting together a site in WordPress and needed to make it so only logged in users could view posts. In addition, only users logged in could download/view images and documents uploaded through the media library (wp-content/uploads/).

Redirect visitors to login page

First part is easy.  Just add this to functions.php:

// Redirect users who are not logged in...
function login_redirect() {
    // Current Page
    global $pagenow;

    // Check if user is not logged in and not on the wp-login page
    if(!is_user_logged_in() && $pagenow != 'wp-login.php' )
	  auth_redirect();
}
add_action( 'wp', 'login_redirect' );

Visitors are now directed to the default WordPress login page.  The default login page is bleak and not attractive to someone happening onto the site.  You could add a logo to it using a plugin or you could use the Theme My Login plugin and make it match the site.  The Theme My Login plugin also takes care of redirecting users to a page outside the normal WordPress admin.  One issue that arises is that we now have 4-5 pages (login, logout, forgot password, register, etc…) created by the Theme My Login plugin that need be visible without logging in.  We need to go back to our login_redirect() function and whitelist those pages from auth_redirect().

// Redirect users who are not logged in...
function login_redirect() {

    // Current page and post obj
    global $pagenow, $post;

    // Theme My Login pages
    $post_ids = array(111, 222, 333, 444);

    // Check if user is not logged in and not on the wp-login page
    if(!is_user_logged_in() && $pagenow != 'wp-login.php' && !in_array($post->ID,$post_ids) )
	  auth_redirect();
}
add_action( 'wp', 'login_redirect' );

Now we have an array of post IDs that are whitelisted from auth_redirect().

Select Page/Post visibility with custom meta field

Of course keeping track of post IDs via manual code updates can be tedious.  A better way is to just add a custom meta field that allow you select if the page requires a login or not.  Advanced Custom Fields is a fantastic plugin that we can use to create that custom meta field.

acf-visibility-field-groupCreate a new field group and call it something like Page Visibility. Fill in the other settings something like:

  • Field Label: This page is visible to
  • Field Name: visibility
  • Field Type: Select
  • Required?: Yes
  • Choices:
    • logged_in : Logged in Users
    • public : Anyone (Public)
  • Default Value: logged_in
  • Location
    • Post Type equal to Page
  • Options
    • Position: Side

New lets go back to the login_redirect function and make some changes.  We run a query to find all posts that are set to public.  Then we loop through then and add the post IDs to the array of post IDs we want to whitelist.

// Redirect users who are not logged in...
function login_redirect() {

    // Current page and post obj
    global $pagenow, $post;

    // get public pages
    $args = array(
    	'post_type' => 'page',
    	'meta_query' => array(
			array(
				'key'     => 'visibility',
				'value'   => 'public',
				'compare' => '=',
			),
		),
		'posts_per_page' => '-1'
    );
    $posts = get_posts($args);
    $post_ids = array();
    foreach ($posts as $_post) {
    	$post_ids[] += $_post->ID;
    }

    // Check if user is not logged in and not on the wp-login page
    if(!is_user_logged_in() && $pagenow != 'wp-login.php' && !in_array($post->ID,$post_ids) )
	  auth_redirect();
}
add_action( 'wp', 'login_redirect' );

 

Restrict media library uploads to logged in users

I found a great piece of code on StackExchange by Hakre: http://wordpress.stackexchange.com/a/37743

This code uses .htaccess to redirect all files in wp-content/uploads/ through a php file that verifies they are logged in before serving the file request.

One thing I ran into is that if you have any images/files on those pages that are whitelisted (or in the header/footer) they will also be blocked also.  To get around that I added a small whitelist array of images to the top of dl-file.php.

require_once('wp-load.php');

global $pagenow;

// whitelist images
$whitelist = array('image01.jpg', 'image02.png');
$logo = get_option('options_logo'); $logo = wp_get_attachment_image_src($logo, 'full'); $whitelist[] = basename($logo[0]);

is_user_logged_in() || in_array(basename($_SERVER['REQUEST_URI']), $whitelist) || auth_redirect();

list($basedir) = array_values(array_intersect_key(wp_upload_dir(), array('basedir' => 1)))+array(NULL);

Note: If you are using Nginx you will need to create a custom vhost file that tells Nginx to pass through whichever files you don’t want it to cache.


Tags: , , ,
Categorized in:
Posted on February 16, 2014

Comments are closed here.