
Action Hook since 2.9.0
wp_scheduled_delete
Fires daily via WP-Cron, right before old trashed posts are purged
Description
wp_scheduled_delete runs once a day as a scheduled WP-Cron event, immediately before WordPress permanently deletes posts that have sat in Trash longer than EMPTY_TRASH_DAYS.
When it runs
Background, triggered by WP-Cron's wp_scheduled_delete cron event — not tied to any front-end or admin request directly.
Signature
do_action( 'wp_scheduled_delete' );Examples
Basic
add_action( 'wp_scheduled_delete', function() {
error_log( 'Trash cleanup cron run started.' );
} );Log every time the trash-cleanup cron event fires.
Real case
add_action( 'wp_scheduled_delete', function() {
// Run a custom cleanup of orphaned postmeta alongside core's trash purge.
global $wpdb;
$wpdb->query( "DELETE pm FROM {$wpdb->postmeta} pm LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id WHERE p.ID IS NULL" );
} );Piggyback custom database cleanup onto WordPress's existing daily cron slot.
Edge case
add_action( 'wp_scheduled_delete', function() {
// Only fires if WP-Cron itself is running — a site with
// DISABLE_WP_CRON set and no real system cron never triggers this.
} );On a site with WP-Cron disabled and no server cron configured, this event simply never fires.
Common Use Cases
- Piggyback custom database cleanup on the daily cron cycle
- Log or monitor trash-purge activity
- Audit how much content is being auto-deleted
- Trigger a backup before old content is purged
Common mistakes
- Assuming this runs at a precise time — WP-Cron is request-triggered, so on low-traffic sites it can be delayed well past its scheduled time
- Not accounting for DISABLE_WP_CRON — if the site relies on a real system cron hitting wp-cron.php, this event depends on that being configured correctly
Related hooks
FAQ
How often does wp_scheduled_delete run?
Once daily, as one of WordPress's built-in scheduled events.
Why didn’t this fire at exactly midnight?
WP-Cron is pseudo-cron — it only checks for due events on incoming site requests, so timing depends on traffic unless a real system cron drives it.
Source: wp-includes/cron.php