Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

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.

share|improve this question
    
From the related questions: wordpress.stackexchange.com/questions/73738/… – did you read that? –  toscho Mar 22 '13 at 9:34
    
Yes, I still get a status 200 with that. –  RRikesh Mar 22 '13 at 9:40

5 Answers 5

up vote 8 down vote accepted
+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.

share|improve this answer
    
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 at 13:08
    
@henrywright it looks like a typo there, I updated the answer, thanks ;-) –  birgire Sep 8 at 13:45

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

share|improve this answer
    
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

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();
    }
}
share|improve this answer
    
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 at 8:26

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; 
    }
}
share|improve this answer
    
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

My solution:

add_action( 'wp', 'my_404' );
function my_404() 
{
    if ( is_404() ) 
    {
        header("Status: 404 Not Found");
        global $wp_query;
        $wp_query->set_404();
        status_header(404);
        nocache_headers();
        //var_dump(getallheaders()); var_dump(headers_list()); die();
        header("location:".homeurl()) or die();
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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