40

I need to force a 404 on some posts based on conditions. I managed to do it ( although I don't know if I did it the right way) and I'm a getting my 404.php template to load as expected.

My code:

function rr_404_my_event() {
  global $post;
  if ( is_singular( 'event' ) && !rr_event_should_be_available( $post->ID ) ) {
    include( get_query_template( '404' ) );
    exit; # so that the normal page isn't loaded after the 404 page
  }
}

add_action( 'template_redirect', 'rr_404_my_event', 1 );

Code 2 from this related question - same problem:

function rr_404_my_event() {
  global $post;
  if ( is_singular( 'event' ) && !rr_event_should_be_available( $post->ID ) ) {
    global $wp_query;
    $wp_query->set_404();
  }
}

add_action( 'wp', 'rr_404_my_event' );

My Issue:

Although it looks good, I get a status 200 OK if I check the network tab. Since it's a status 200, I am afraid that search engines might index those pages too.

Expected Behaviour:

I want a status 404 Not Found to be sent.

51
+150

You could try the Wordpress function status_header() to add the HTTP/1.1 404 Not Found header;

So your Code 2 example would be:

function rr_404_my_event() {
  global $post;
  if ( is_singular( 'event' ) && !rr_event_should_be_available( $post->ID ) ) {
    global $wp_query;
    $wp_query->set_404();
    status_header(404);
  }
}
add_action( 'wp', 'rr_404_my_event' );

This function is for example used in this part:

function handle_404() {
    ...cut...
    // Guess it's time to 404.
    $wp_query->set_404();
    status_header( 404 );
    nocache_headers();
    ...cut...
}

from the wp class in /wp-includes/class-wp.php.

So try using this modified Code 2 example in addition to your template_include code.

  • The Code 2 snippet you posted works perfectly. The set_header() was what was missing. – RRikesh Mar 25 '13 at 5:11
  • @birgire you refer to set_header() to add HTTP/1.1 404 Not Found but have used status_header() in your code? – henrywright Sep 8 '14 at 13:08
  • @henrywright it looks like a typo there, I updated the answer, thanks ;-) – birgire Sep 8 '14 at 13:45
15

This code worked for me:

add_action( 'wp', 'force_404' );
function force_404() {
    global $wp_query; //$posts (if required)
    if(is_page()){ // your condition
        status_header( 404 );
        nocache_headers();
        include( get_query_template( '404' ) );
        die();
    }
}
  • Handy. I'm checking for custom query parameters so I'm not using the action, but it makes for a very useful method in my plugin class. – John Reid Oct 17 '14 at 8:26
  • 2
    Add the following to fix the page title: global $wp_query; $wp_query->is_404 = true; – developerbmw Feb 24 '16 at 22:18
2

I wouldn't recommend forcing a 404.

If you're worried about search engines why not just do a "no-index,no-follow" meta on those pages and block it with robots.txt?

This may be a better way to block the content from being viewed

add_filter( 'template_include', 'nifty_block_content', 99 );

function nifty_block_content( $template ) {
  if ( is_singular( 'event' ) && !rr_event_should_be_available( $post->ID ) ) {
        $template = locate_template( array( 'nifty-block-content.php' ) );
     }
    return $template;
}

You could probably also use this method to load 404.php but I feel that using a page template might be a better option.

source

  • Thanks a lot for the link, I'll switch to using locate_template() instead. I think that robots.txt. isn't a guaranteed way to protect from indexation. Some search engines might still pick up the page. I do want the page to look like a normal 404 page. Also the posts are going to be dynamically added, editing the robots.txt file will add more trouble. – RRikesh Mar 22 '13 at 9:23
1

My solution:

add_action( 'wp', 'my_404' );
function my_404() 
{
    if ( is_404() ) 
    {
        header("Status: 404 Not Found");
        $GLOBALS['wp_query']->set_404();
        status_header(404);
        nocache_headers();
        //var_dump(getallheaders()); var_dump(headers_list()); die();
    }
}
  • 1
    Redirecting on errors is terrible for your page ranking. Just show a template at the same location as the bad request. What will happen when you do that is you initially set a 404, and then the redirect alters it to a 301 or 302, which then redirects to a page that returns a 200. That then gets indexed by search engines as a valid page, which is explicitly what OP said he didn't want. – mopsyd Mar 15 '18 at 1:43
0

Status codes are sent in the headers of HTTP requests. Your current function is hooked into a hook that will be called too late.

You should try to hook your function rr_404_my_event() into action send_headers.

I'm not sure if at that point in time it's even possible to check the Post ID, but give this a go:

add_action( 'send_headers', 'rr_404_my_event' );
function rr_404_my_event() {
    global $post;
    if ( is_singular( 'event' ) && !rr_event_should_be_available( $post->ID ) ) {
        include( get_query_template( '404' ) );
        header('HTTP/1.0 404 Not Found');
        exit; 
    }
}
  • I corrected some syntax errors from your codes. I don't even get my 404 template to load with that. – RRikesh Mar 22 '13 at 10:48
  • Perhaps, in your 404.php you could load a different header.php, e.g. <?php get_header('404'); ?> to load header-404.php. In that header, you'd add header('HTTP/1.0 404 Not Found'); in the <head> section. – Marc Dingena Mar 22 '13 at 11:07
0

I wanted to share the way I used the marked solution

function fail_safe_for_authors() {
    if ((is_user_logged_in()) && (is_author()) && ($_COOKIE["user_role"] !== "administrator")) {
            global $wp_query;
            $wp_query->set_404();
            status_header(404);
        }
}
add_action("wp", "fail_safe_for_authors");

I did this to separate all user types from the administrator, in this project, Only the admin can see the author.php page.

I hope it could help somebody else.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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