404 page is not the same when using PHP code in functions

template_include is a filter hook, so you are supposed to always return the full absolute filesystem path to a template, and not doing the get_template_part() and exit calls.

And as for setting the 404 status, I would instead use the pre_handle_404 filter, but the wp action can also be used: (note that these examples are not using PHP 8 syntaxes/functions, and secondly, I’m checking against the request path and not the full URL)

  • Using the pre_handle_404 filter:

    add_filter( 'pre_handle_404', 'my_filter_pre_handle_404', 1, 2 );
    function my_filter_pre_handle_404( $preempt, $wp_query ) {
        global $wp;
        if ( false !== strpos( $wp->request, '/page/' ) ) {
            $wp_query->set_404();
            status_header( 404 );
        }
    
        return $preempt;
    }
    
  • Using the wp action:

    add_action( 'wp', 'my_action_wp', 1 );
    function my_action_wp( $wp ) {
        global $wp_query;
        if ( false !== strpos( $wp->request, '/page/' ) ) {
            $wp_query->set_404();
            status_header( 404 );
        }
    }
    

So whichever option you chose, you should now see the proper 404 headers and template, just like what we would see when visiting a page which truly did not exist.