Skip to content

Instantly share code, notes, and snippets.

@vapvarun
Created June 4, 2026 03:11
Show Gist options
  • Select an option

  • Save vapvarun/ccb6d31565648f8aaca3a6a034e6593b to your computer and use it in GitHub Desktop.

Select an option

Save vapvarun/ccb6d31565648f8aaca3a6a034e6593b to your computer and use it in GitHub Desktop.
WordPress shutdown action gotchas: headers, object cache, timeouts - tweakswp.com tutorial
<?php
/**
* Common gotchas when using the WordPress shutdown action
*
* Covers: headers-already-sent, object cache availability,
* timeout behavior, and output buffer state.
*
* Tutorial: https://tweakswp.com/
*/
// --- Gotcha 1: Headers are already sent on shutdown ---
// wp_redirect(), wp_die(), and header() will have no effect.
// Do not call them on the shutdown action.
add_action( 'shutdown', function () {
// BAD — headers already sent:
// wp_redirect( home_url() ); // This will trigger a "headers already sent" warning.
// GOOD — write to storage instead of redirecting:
update_option( 'my_plugin_last_run', time() );
} );
// --- Gotcha 2: Object cache may have been cleared ---
// If your caching layer (Redis, Memcache) flushes at the end of the request,
// wp_cache_get() on shutdown may return false even for keys you set earlier.
// Always fall back gracefully.
add_action( 'shutdown', function () {
$cached = wp_cache_get( 'my_data_key', 'my_group' );
if ( false === $cached ) {
// Cache is gone. Do a direct database read if needed.
global $wpdb;
$cached = $wpdb->get_var( "SELECT option_value FROM {$wpdb->options} WHERE option_name = 'my_option'" );
}
// Use $cached safely.
} );
// --- Gotcha 3: max_execution_time is not paused on shutdown ---
// PHP's max_execution_time counter keeps running through shutdown callbacks.
// If your request already took 28 seconds and max_execution_time is 30,
// your shutdown callback has 2 seconds before a timeout fatal.
// Extend it explicitly when running expensive tasks:
add_action( 'shutdown', function () {
set_time_limit( 60 ); // Reset the clock for the shutdown work.
ignore_user_abort( true ); // Keep running if the browser closed the connection.
// ... expensive background work ...
}, 10 );
// --- Gotcha 4: Plugins hooked at shutdown priority 999 run after you ---
// WordPress hooks run in priority order. Several caching plugins (e.g. W3TC,
// WP Super Cache) flush their page cache buffer at 'shutdown' with late priorities.
// If your code depends on the response being fully assembled, use a late priority.
add_action( 'shutdown', 'tweakswp_late_cleanup', 100 );
function tweakswp_late_cleanup(): void {
// Runs after most plugins have finished their shutdown work.
}
// --- Gotcha 5: $wpdb connection may close before shutdown completes ---
// On high-load servers, the MySQL connection can time out during a long
// shutdown callback. Re-check the connection if you run queries after heavy work.
add_action( 'shutdown', function () {
global $wpdb;
// ... do long work first ...
// Re-ping if queries might follow after a long delay:
if ( $wpdb->check_connection() ) {
$wpdb->insert( $wpdb->prefix . 'my_log', [ 'event' => 'done', 'ts' => time() ] );
}
} );
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment