if/else on custom query gives 200 OK when condition not met?

What? I am modifying my theme to show comments of a post on a separate page.

How? Mostly based on this answer but with improved checks and code. So this is what my page-comments.php (kinda) looks like:

Read More

(The code is very easy to scan through, trust me!)

<?php get_header(); ?>

<?php
    /*
     * (Example URL: example.com/comments/?original_post=ID)
     *
     * Check if HTTP variable (original_post) exists in queried URL,
     * AND that it is set to some value (ID), AND that the value (ID) is a number.
     */
    if( array_key_exists( 'original_post', $_GET ) && isset( $_GET['original_post'] ) && ctype_digit( $_GET['original_post'] ) ) {

        $query_original_post = new WP_Query( array(
            'posts_per_page' => 1,
            'p'              => $_GET['original_post'],
            'post_type'      => 'any',
        ) );

        if( $query_original_post->have_posts() ) {

            while ( $query_original_post->have_posts() ) : $query_original_post->the_post();

                // [...]

            endwhile;
            wp_reset_postdata();

        } else {

            /*
             * Return 404 error if there are no posts
             */
            global $wp_query;
            $wp_query->set_404();
            status_header( 404 );
            nocache_headers();
            get_template_part( '404', 'archive' );
            //exit();

        }

    } else {

        /*
         * Return 404 error if the HTTP variable (original_post) doesn't
         * exist in queried URL, OR it isn't set to any value (ID),
         * OR the value (ID) isn't a number.
         */
        global $wp_query;
        $wp_query->set_404();
        status_header( 404 );
        nocache_headers();
        get_template_part( '404', 'archive' );
        //exit();

    }
?>

<?php get_footer(); ?>

(The code I use to return a 404 error is based on these two solutions.)

As you can see I am doing two checks in the code (at least that’s what I intended to do):

  1. Check if HTTP variable (original_post) exists in queried URL, AND that it is set to some value (ID), AND that the value (ID) is a number. OTHERWISE, return a 404 error with appropriate headers.

  2. Check if the custom WordPress query ($query_original_post) has any results to loop over. OTHERWISE, return a 404 error with appropriate headers.

Problem:

  1. Is that the right way to do it? (or at least, can it be any simpler?)

  2. (If yes to #1) It appears to work, but returns 200 OK header when I go to an URL like: example.com/comments/?original_post=NUMBER, where NUMBER is any positive integer over 4 digits long (e.g. 14532).

    I don’t understand the relation. Why’d something like “example.com/comments/?original_post=14532` return 200 OK header when I am logged-into WordPress, and 404 error (which it should) when I am not?

Related posts

1 comment

  1. Headers are sent long before you try to alter them. Headers are sent by actions associated with get_header(), so by the time your code executes, it is too late to alter the headers. You can demonstrate this with a simple experiment. Try each of the following:

    get_header(); 
    status_header( 404 );
    

    and

    status_header( 404 );
    get_header(); 
    

    In a template file and watch the output with a tool like HttpFox or Wireshark (if you really want to have some fun 🙂 ).

    To effectively change the headers you will need to run your logic before get_header().

    I believe this will get the effect you want with fewer lines of code to boot:

    /*
    * (Example URL: example.com/comments/?original_post=ID)
    *
    * Check if HTTP variable (original_post) exists in queried URL,
    * AND that it is set to some value (ID), AND that the value (ID) is a number.
    */
    
    if( 
      array_key_exists( 'original_post', $_GET ) 
      && isset( $_GET['original_post'] ) 
      && ctype_digit( $_GET['original_post'] ) 
    ) {
      $query_original_post = new WP_Query( 
        array(
          'posts_per_page' => 1,
          'p'              => $_GET['original_post'],
          'post_type'      => 'any',
        ) 
      );
      if( !$query_original_post->have_posts() ) {
        global $wp_query;
        $wp_query->set_404();
        status_header( 404 );
        nocache_headers();
      } 
    }
    
    get_header();
    if( !empty($query_original_post) && $query_original_post->have_posts() ) {
      while ( $query_original_post->have_posts() ) {
        $query_original_post->the_post();
        // [...]
      }
      wp_reset_postdata();
    } else {
      get_template_part( '404', 'archive' );
    }
    get_footer();
    

    And for the love of all that is holy please don’t use PHP opening and closing tags unless you actually need them.

Comments are closed.