How to 301 private posts rather than 404?

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:

Read More
add_action('wp','redirect_stuffs', 0);
function redirect_stuffs(){
global $post;
    if ( $post->post_status == "private" && !is_admin() ):
        wp_redirect("http://dangayle.com/",301);
        exit();
    endif;
}

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.

Related posts

Leave a Reply

3 comments

  1. 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 );
            exit();
        endif;
    }
    

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

  2. 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.”

  3. 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'] );
            else
                $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 );
        exit;
    }
    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).