Skip to content

Instantly share code, notes, and snippets.

@MikeNGarrett
Created October 13, 2017 20:11
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save MikeNGarrett/f252e16e407f6eecbe0139be71f67223 to your computer and use it in GitHub Desktop.
Save MikeNGarrett/f252e16e407f6eecbe0139be71f67223 to your computer and use it in GitHub Desktop.
Set cache headers on WordPress 404 pages.
<?php
/**
* Force cache headers on 404 pages and prevent WordPress from handling 404s.
*
* @param bool $preempt determines who handles 404s.
* @param obj $wp_query global query object.
*/
function change_404_headers( $preempt, $wp_query ) {
if ( ! is_admin() && ! is_robots() && count( $wp_query->posts ) < 1 ) {
header( 'Cache-Control: max-age=30000, must-revalidate' );
header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', strtotime( '+5000 minutes' ) ) . ' GMT' );
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s', strtotime( '-5000 minutes' ) ) . ' GMT' );
$wp_query->set_404();
status_header( 404 );
// prevents the default 404 from firing.
return true;
}
return $preempt;
}
add_filter( 'pre_handle_404', 'change_404_headers', 2, 99 );
@kshaner
Copy link

kshaner commented May 9, 2018

The issue discovered is when the site is using a permalink structure where the first variable is one of: postname, category, tag, or author. This throws WP_Rewrite into 'use_verbose_page_rules' mode. This means that WP_Query is always in a state of error because no rewrite rule matches but the query still runs and performs a standard blog query where the most recent posts are returned but the query still calls $wp_query->set_404. This causes the count( $wp_query->posts ) < 1 check to fail.

Also, the action and argument count arguments are inverted.

@slim16165
Copy link

Is there any update on the issue? @MikeNGarrett @kshaner

@AfterAllDev
Copy link

@slim16165 Here's something that I've been trying out today and seems to work so far:

add_action('nocache_headers', function($headers){
    if (is_404() ) {
        /// Unset the WordPress "nocache" headers    
        unset( $headers['Expires'] );
        unset( $headers['Cache-Control'] );
        // Alternatively set new headers
        $headers['Cache-Control'] = 'max-age=300';
    }
    return $headers;
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment