When a non-logged-in reader visits a private WordPress post or page, she gets a 404 “not found” error message, as if the post didn’t exist at all. That’s great if you don’t want the world to know that the post exists, but what if you’re doing something less clandestine, like members-only content? You do want people to know the post is there–they haven’t followed a bad link!–but they need to log in to see it. Then “not found” is misleading.
There are two ways you could make the user experience better: redirecting private 404s to the login screen with a message, or changing the 404 error text.
Redirecting private 404s to the login screen
If you’re going with the redirect option, you’ll need two small functions in your custom plugin or your theme’s functions.php file. The first checks whether the queried object (in this case, the post/page) is private, and if so, redirects to the login URL with a custom query argument and instructions to return the user to the original page after they’ve logged in.
The second function runs on the login screen. If the custom query argument is set, it adds a message telling the user they need to log in before viewing the original page.
https://gist.github.com/sillybean/dfd838ef39ff72e83420b8182a0f7e3e
Changing the 404 error text (in Genesis)
If you want to be a little less presumptuous about whisking users off to a login screen, you can instead filter the error message and give them the option of following the login link.
In most WordPress themes, you can edit your template files directly to add a wp_login_url() link. In Genesis, you need to filter the noposts text instead:
https://gist.github.com/sillybean/b5894768608d4afa3636f64ba73b6e6f
Bill Erickson says
I tried implementing the first part (redirecting private 404s) but it didn’t work for me.
I believe `init` is too early a hook because the query hasn’t been parsed yet. I used `wp` instead. When I was logged in I got the appropriate object from `get_queried_object()`, but when I was logged out the object was empty.
Stephanie Leary says
Thanks! I’ll update the Gist. On Genesis sites, I used the ‘genesis’ hook instead.
luca says
Hello Bill, I use a template created with 404.php Extender Custom, I changed some values to not display the list
This code:
<?php
/**
* Genesis Framework.
*
* WARNING: This file is part of the core Genesis Framework. DO NOT edit this file under any circumstances.
* Please do all modifications in the form of a child theme.
*
* @package Genesis\Templates
* @author StudioPress
* @license GPL-2.0+
* @link http://my.studiopress.com/themes/genesis/
*/
//* Remove default loop
remove_action( 'genesis_loop', 'genesis_do_loop' );
add_action( 'genesis_loop', 'genesis_404' );
/**
* This function outputs a 404 "Not Found" error message
*
* @since 1.6
*/
function genesis_404() {
echo genesis_html5() ? '' : '';
printf( '%s', apply_filters( 'genesis_404_entry_title', __( 'Not found, error 404', 'genesis' ) ) );
echo '';
echo apply_filters( 'genesis_404_entry_content', '' . sprintf( __( 'The page you are looking for no longer exists. Perhaps you can return back to the site\'s homepage and see if you can find what you are looking for. Or, you can try finding it by using the search form below.', 'genesis' ), trailingslashit( home_url() ) ) . '' );
get_search_form();
}
genesis();
I have some pages / post set as “private” and I wish you were notified that it is a private page therefore requires the login to see it, always if a user associated with the profile private.
If you copy your second code in function.php file without <?php:
add_filter( 'genesis_noposts_text', 'my_custom_404_message', 10, 2 );
function my_custom_404_message( $text ) {
$queried_object = get_queried_object();
if ( isset( $queried_object->post_status ) && 'private' == $queried_object->post_status && !is_user_logged_in() )
$text = sprintf( __( 'This page is restricted. Please log in or register.' ), wp_login_url( $_SERVER['REQUEST_URI'] ) );
elseif ( is_search() )
$text = __( 'I could not find any entries that matched your search. Would you like to try again?' ) . get_search_form( false );
else
$text = __( 'There are no entries in this section. Would you like to search the site?' ) . get_search_form( false );
return $text;
}
always by obtaining the previous 404.php page, it seems that the code is not working .. I have to delete the 404.php template to use this feature?
thanks
_BenM_ says
Hi,
Unfortunatly, this doesn’t work on bbPress forums and topics : get_queried_object() is empty.