When you consolidate two posts, a 301 redirect sends the traffic to the survivor but leaves the comment thread stranded on the old post. There is no well-maintained plugin for this, so this DevAnswers guide moves the comments by reassigning their comment_post_ID, three ways from easiest to most technical: a one-time PHP snippet, phpMyAdmin, or WP-CLI.
mysqldump. If you have a staging site, try it there first. Method 1: A One-Time PHP Snippet (Easiest)
All three methods need two post IDs: the source (where the comments are now) and the target (where they should go). Hover the Edit link for each post under Posts and read the post= number in the URL. The examples use source 123 and target 456; replace them with your own.
If you would rather not touch the database, run a small function once. It goes through WordPress, so it fires the normal hooks and rebuilds the comment counts as part of the move. Add it with the free Code Snippets plugin, or paste it into your theme’s functions.php:
add_action( 'init', function () {
if ( get_option( 'merge_comments_done' ) ) {
return; // already run once
}
$source = 123;
$target = 456;
$ids = get_comments( array(
'post_id' => $source,
'status' => 'approve',
'type' => 'comment',
'fields' => 'ids',
) );
foreach ( $ids as $id ) {
wp_update_comment( array(
'comment_ID' => $id,
'comment_post_ID' => $target,
) );
}
wp_update_comment_count_now( $source );
wp_update_comment_count_now( $target );
update_option( 'merge_comments_done', 1 );
} );
Load any page of your site once to run it, then delete the snippet (you can drop the merge_comments_done option afterward too). get_comments() with 'status' => 'approve' and 'type' => 'comment' fetches only the real, approved comments (no spam, trash, or pingbacks), and wp_update_comment() re-points each to the target. The two wp_update_comment_count_now() calls then rebuild the cached comment total on both posts, so the snippet leaves the counts correct with no extra step. The merge_comments_done flag stops it repeating if you forget to remove it.
max_execution_time) on a post with many thousands of comments. For a very large thread, use the phpMyAdmin query below (one statement, no page-load limit) or WP-CLI instead. Method 2: phpMyAdmin (No SSH Needed)
If you have database access (cPanel, Plesk, or your provider’s panel), do it in two queries. In phpMyAdmin, open the SQL tab and run the move:
UPDATE wp_comments
SET comment_post_ID = 456
WHERE comment_post_ID = 123
AND comment_type IN ('', 'comment')
AND comment_approved = '1';
The wp_comments table records which post each comment belongs to in its comment_post_ID column, so changing that value reassigns the comments; the author, date, and content travel with each one. The conditions move only real, approved comments: comment_approved = '1' skips spam, trash, and pending, while comment_type IN ('', 'comment') moves regular comments and leaves pingbacks, trackbacks, and any custom comment types behind. (The empty string is included because older WordPress stores regular comments with an empty comment_type, so = 'comment' alone would miss them.) Adjust the table name if your prefix is not wp_.
WordPress caches each post’s comment total in the wp_posts table for speed. A raw SQL query updates the comments but not that cached count, so without a second query the old post still shows the comments and the new one shows zero. Rebuild both counts (the PHP snippet and the WP-CLI method below each do their own recount; this is the SQL equivalent):
UPDATE wp_posts
SET comment_count = (
SELECT COUNT(*) FROM wp_comments
WHERE comment_post_ID = wp_posts.ID
AND comment_approved = '1'
)
WHERE ID IN (123, 456);
That recalculates comment_count (which counts approved comments) for both posts straight from the comment rows; it reads wp_comments while updating wp_posts, so it is safe to run. If you run a persistent object cache or full-page cache, clear it, then reload each post to confirm. For other database edits, see our guide to find and replace URLs in the WordPress database.
Method 3: WP-CLI (Fastest, Needs SSH)
With SSH access, WP-CLI suits large threads well, since it runs in the terminal rather than on a page load. Many managed hosts ship it already, so run wp --info first; if it is missing, install it:
curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar
chmod +x wp-cli.phar
sudo mv wp-cli.phar /usr/local/bin/wp
(No sudo? Keep wp-cli.phar in your home directory and call it as php wp-cli.phar instead of wp.) Run the commands below from your WordPress directory (the folder with wp-config.php), or add --path=/path/to/wordpress to each. List the source post’s approved comment IDs, reassign each to the target, then recount both posts:
wp comment list --post_id=123 --status=approve --type=comment --field=comment_ID | \
xargs wp comment update --comment_post_ID=456
wp comment recount 123 456
The list command uses comment_ID (the actual column name) with --status=approve --type=comment, so it moves the same scope as the other methods: only approved, real comments (no spam, trash, or pingbacks). Piping the IDs into a single wp comment update reassigns them all in one call (no extra -I %, which would spawn a separate process per comment), and wp comment recount then rebuilds the cached total on both the source and target posts.
Summary
Two things hold true for every method: the moved comments keep their existing status, and only the current thread moves. New comments still land on whichever post they are submitted under, so if you are retiring the old post, redirect it so visitors comment on the survivor from now on.
In short: back up first, then reassign the comments by changing comment_post_ID. The easiest route is the one-time PHP snippet (via Code Snippets or functions.php), which moves the approved comments and recounts itself. With phpMyAdmin you run the move query then a second query to rebuild comment_count; with WP-CLI you run wp comment update then wp comment recount. Whatever the method, the move and the recount are the two halves to remember.
Comments