Skip to content

Instantly share code, notes, and snippets.

@vapvarun
Created February 23, 2026 19:37
Show Gist options
  • Select an option

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

Select an option

Save vapvarun/ebfead49dfc4de63c0c13a23ca8e3eca to your computer and use it in GitHub Desktop.
WordPress Post Revisions: Limit, Disable, Clean Up (tweakswp.com)
SELECT COUNT(*) FROM wp_posts WHERE post_type = 'revision';
// Keep only the 5 most recent revisions per post
define( 'WP_POST_REVISIONS', 5 );
// Disable post revisions entirely
define( 'WP_POST_REVISIONS', false );
// Or equivalently:
define( 'WP_POST_REVISIONS', 0 );
/**
* Limit revisions to 3 for all post types.
*
* @param int $num Number of revisions to keep.
* @param WP_Post $post The post object.
* @return int
*/
add_filter( 'wp_revisions_to_keep', function( $num, $post ) {
return 3;
}, 10, 2 );
add_filter( 'wp_revisions_to_keep', function( $num, $post ) {
$limits = array(
'post' => 3, // Blog posts: 3 revisions
'page' => 10, // Pages: 10 revisions (more critical content)
'product' => 5, // WooCommerce products: 5 revisions
);
if ( isset( $limits[ $post->post_type ] ) ) {
return $limits[ $post->post_type ];
}
return 3; // Default for all other post types
}, 10, 2 );
add_filter( 'wp_revisions_to_keep', function( $num, $post ) {
if ( 'event' === $post->post_type ) {
return 0; // No revisions for events
}
return $num; // Keep default for everything else
}, 10, 2 );
# Using WP-CLI (recommended)
wp db export backup-before-revision-cleanup.sql
# Using mysqldump directly
mysqldump -u db_username -p db_name > backup-before-revision-cleanup.sql
# Count all revisions via WP-CLI
wp post list --post_type=revision --format=count
# Or via SQL
wp db query "SELECT COUNT(*) as revision_count FROM wp_posts WHERE post_type = 'revision';"
# Delete ALL revisions
wp post delete $(wp post list --post_type=revision --format=ids) --force
# Delete revisions older than 30 days only
wp post list --post_type=revision --format=ids --before="30 days ago" | xargs wp post delete --force
# Delete revisions in batches of 500
while ids=$(wp post list --post_type=revision --format=ids --posts_per_page=500); do
if [ -z "$ids" ]; then
break
fi
wp post delete $ids --force
echo "Deleted batch. Remaining: $(wp post list --post_type=revision --format=count)"
sleep 1
done
-- Step 4a: Delete all revision posts
DELETE FROM wp_posts WHERE post_type = 'revision';
-- Step 4b: Clean up orphaned postmeta (meta for deleted posts)
DELETE pm FROM wp_postmeta pm
LEFT JOIN wp_posts p ON p.ID = pm.post_id
WHERE p.ID IS NULL;
-- Step 4c: Clean up orphaned term relationships
DELETE tr FROM wp_term_relationships tr
LEFT JOIN wp_posts p ON p.ID = tr.object_id
WHERE p.ID IS NULL;
-- Delete in batches for very large databases
DELETE FROM wp_posts WHERE post_type = 'revision' LIMIT 5000;
# Via WP-CLI
wp db optimize
# Via SQL
OPTIMIZE TABLE wp_posts, wp_postmeta, wp_term_relationships;
// Autosave every 3 minutes instead of every 60 seconds
define( 'AUTOSAVE_INTERVAL', 180 );
/**
* Database Optimization: Revisions & Autosaves
*/
// Limit post revisions to 5 per post
define( 'WP_POST_REVISIONS', 5 );
// Increase autosave interval to 3 minutes (180 seconds)
define( 'AUTOSAVE_INTERVAL', 180 );
<?php
/**
* Plugin Name: TweaksWP Revision Cleanup
* Description: Automatically cleans up old post revisions weekly.
* Version: 1.0.0
*/
// Schedule the cleanup event on plugin activation
register_activation_hook( __FILE__, function() {
if ( ! wp_next_scheduled( 'tweakswp_cleanup_revisions' ) ) {
wp_schedule_event( time(), 'weekly', 'tweakswp_cleanup_revisions' );
}
});
// Unschedule on deactivation
register_deactivation_hook( __FILE__, function() {
wp_clear_scheduled_hook( 'tweakswp_cleanup_revisions' );
});
// The cleanup callback
add_action( 'tweakswp_cleanup_revisions', function() {
global $wpdb;
// Delete revisions older than 30 days, in batches of 1000
$deleted = $wpdb->query(
"DELETE FROM {$wpdb->posts}
WHERE post_type = 'revision'
AND post_date < DATE_SUB( NOW(), INTERVAL 30 DAY )
LIMIT 1000"
);
// Clean up orphaned postmeta
$wpdb->query(
"DELETE pm FROM {$wpdb->postmeta} pm
LEFT JOIN {$wpdb->posts} p ON p.ID = pm.post_id
WHERE p.ID IS NULL
LIMIT 1000"
);
if ( defined( 'WP_DEBUG' ) && WP_DEBUG ) {
error_log( sprintf(
'TweaksWP Revision Cleanup: Removed %d old revisions.',
$deleted
));
}
});
# Total revision count
wp post list --post_type=revision --format=count
# Top 10 posts with the most revisions
wp db query "SELECT post_parent, COUNT(*) as rev_count
FROM wp_posts
WHERE post_type = 'revision'
GROUP BY post_parent
ORDER BY rev_count DESC
LIMIT 10;"
# Full database table sizes
wp db size --tables --human-readable
# Just the wp_posts table
wp db query "SELECT
table_name,
ROUND(data_length / 1024 / 1024, 2) AS data_mb,
ROUND(index_length / 1024 / 1024, 2) AS index_mb
FROM information_schema.tables
WHERE table_schema = DATABASE()
AND table_name = 'wp_posts';"
// Debug: check if something overrides your revision limit
add_action( 'init', function() {
$post = get_post( 123 ); // Replace with a real post ID
if ( $post ) {
$keep = wp_revisions_to_keep( $post );
error_log( 'Revisions to keep for post 123: ' . $keep );
}
});
// The constant must appear BEFORE this line in wp-config.php:
/* That's all, stop editing! Happy publishing. */
# Bash loop for batched SQL deletion
while true; do
result=$(wp db query "DELETE FROM wp_posts WHERE post_type = 'revision' LIMIT 5000;" 2>&1)
echo "$result"
# Check if no rows were affected
if echo "$result" | grep -q "0 rows"; then
echo "Cleanup complete."
break
fi
sleep 2
done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment