How do I 301 redirect private pages, rather than 404 them? If a post is private, WordPress filters it out in the SQL query, so there are no $post variables to work with.

I'd like for this code to work, but doesn't:

add_action('wp','redirect_stuffs', 0);
function redirect_stuffs(){
global $post;
    if ( $post->post_status == "private" && !is_admin() ):

I don't know where that is set earlier than wp, other than the fact that I know it's a user role issue. If I could set a non-logged in user to have that capability, it would probably fix the issue:

$publicReader -> add_cap('read_private_posts');

The problem with add_cap is that only logged-in users have capabilities.


3 Answers 3


Sorry guys, I found my answer:

add_action('wp','redirect_stuffs', 0);
function redirect_stuffs(){
global $wpdb; 
    if ($wpdb->last_result[0]->post_status == "private" && !is_admin() ):
        wp_redirect( home_url(), 301 );

Posts/Pages are removed from the sitemaps, but the page still shows up on the site so that it can get 301'd.


It seems to me that you shouldn't return either 404 or 301 - you should return either 401 (Unauthorized / authentication required, though you won't accept any authentication offered) or 403 (Rejected, aka I know what you're asking for and you can't have it).

There's an abandoned plugin Private Page Forbidden that might be worth looking at for guidance, though at a casual glance it seems to want to convert 404 into 403 which seems like a bad idea. Unfortunately while there's been a variety of discussion of options (see https://core.trac.wordpress.org/ticket/10551 and related) the milestone for fixes has gradually moved to "Future Release."

  • I just now understood what you meant. In general, I might agree with you. After all, the return numbers exist for a reason. Unfortunately, in this case, I don't have a choice but to 301, which is a directive which comes from above.
    – Dan Gayle
    Commented Mar 14, 2011 at 15:59

Firstly, I would have to agree with @fencepost's answer. However, I couldn't resist posting a solution, so here we are!

function __intercept_private_page( $posts, &$wp_query )
    // remove filter now, so that on subsequent post querying we don't get involved!
    remove_filter( 'the_posts', '__intercept_private_page', 5, 2 );

    if ( !( $wp_query->is_page && empty($posts) ) )
        return $posts; // bail, not page with no results

    // if you want to explicitly check it *is* private, use the code block below:   
        if ( !empty( $wp_query->query['page_id'] ) )
            $page = get_page( $wp_query->query['page_id'] );
            $page = get_page_by_path( $wp_query->query['pagename'] );

        if ( $page && $page->post_status == 'private' ) {
            // redirect

    // otherwise assume that if the request was for a page, and no page was found, it was private
    wp_redirect( home_url(), 301 );
is_admin() || add_filter( 'the_posts', '__intercept_private_page', 5, 2 );

Update: Revised code to use the_posts filter instead of posts_results (which fires before WordPress checks permissions, and so $posts hasn't been 'emptied' yet).

  • This still 404s.
    – Dan Gayle
    Commented Mar 14, 2011 at 21:44
  • Revised answer. Commented Mar 14, 2011 at 22:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.