So you want to duplicate pages/posts in WordPress.
Move your hand away from the Plugin aisle. Here’s a quick code snippet to make it happen in a few kilobytes.
// Add clone links to post row actions
function hh_add_clone_link($actions, $post) {
if (current_user_can('edit_posts')) {
$actions['clone_draft'] = '<a title="Clone this item to draft" href="' . wp_nonce_url('admin.php?action=hh_clone_post&post=' . $post->ID . '&status=draft', 'clone_post_' . $post->ID) . '">Clone to Draft</a>';
$actions['clone_published'] = '<a title="Clone this item to published" href="' . wp_nonce_url('admin.php?action=hh_clone_post&post=' . $post->ID . '&status=publish', 'clone_post_' . $post->ID) . '">Clone to Published</a>';
}
return $actions;
}
add_filter('post_row_actions', 'hh_add_clone_link', 10, 2);
add_filter('page_row_actions', 'hh_add_clone_link', 10, 2);
// Add clone links to post edit screen
function hh_add_clone_link_edit() {
global $post;
if (current_user_can('edit_posts')) {
echo '
<style>
.cloning-control .button {
font-size: 12px;
display: inline-flex;
align-items: center;
}
</style>
';
echo '<div id="duplicate-action" class="misc-pub-section cloning-control">'
. '<a class="submitduplicate duplication button" title="Clone this item to draft" href="' . wp_nonce_url('admin.php?action=hh_clone_post&post=' . $post->ID . '&status=draft', 'clone_post_' . $post->ID) . '">Clone to Draft</a> '
. '<a class="submitduplicate duplication button" title="Clone this item to published" href="' . wp_nonce_url('admin.php?action=hh_clone_post&post=' . $post->ID . '&status=publish', 'clone_post_' . $post->ID) . '">Clone to Published</a>'
. '</div>';
}
}
add_action('post_submitbox_misc_actions', 'hh_add_clone_link_edit');
// Handle post cloning
function hh_clone_post() {
if (!isset($_GET['post']) || !isset($_REQUEST['action']) || 'hh_clone_post' !== $_REQUEST['action']) {
wp_die('No post to clone has been supplied!');
}
// Nonce verification
if (!isset($_GET['_wpnonce']) || !wp_verify_nonce($_GET['_wpnonce'], 'clone_post_' . $_GET['post'])) {
wp_die('Security check failed.');
}
// Get original post
$post_id = intval($_GET['post']);
$post = get_post($post_id);
if (!$post) {
wp_die('Post does not exist!');
}
// Determine new status
$new_status = isset($_GET['status']) && $_GET['status'] === 'publish' ? 'publish' : 'draft';
// Prepare new post data
$new_post = array(
'post_title' => $post->post_title . ' (Copy)',
'post_content' => $post->post_content,
'post_excerpt' => $post->post_excerpt,
'post_status' => $new_status,
'post_type' => $post->post_type,
'post_author' => $post->post_author,
'post_parent' => $post->post_parent,
);
// Insert new post
$new_post_id = wp_insert_post($new_post);
if (is_wp_error($new_post_id) || !$new_post_id) {
wp_die('Failed to clone the post.');
}
// Copy taxonomies
$taxonomies = get_object_taxonomies($post->post_type);
foreach ($taxonomies as $taxonomy) {
$post_terms = wp_get_object_terms($post_id, $taxonomy, array('fields' => 'slugs'));
wp_set_object_terms($new_post_id, $post_terms, $taxonomy, false);
}
// Copy meta fields
$post_meta = get_post_meta($post_id);
foreach ($post_meta as $key => $value) {
if (in_array($key, array('_wp_old_slug', '_edit_lock', '_edit_last'))) {
continue;
}
update_post_meta($new_post_id, $key, maybe_unserialize($value[0]));
}
// Redirect to new post edit screen
wp_redirect(admin_url('post.php?action=edit&post=' . $new_post_id));
exit;
}
add_action('admin_action_hh_clone_post', 'hh_clone_post');
Before you do anything, be sure you:
Also:
We pair the ease of WordPress with the unparalleled speed and security of a Static live site.
Manage your site’s data and content via WordPress, convert it to Static instantly, and push it live on our hosting platform.
Disclaimer: This code is provided raw and decoupled, for your use and exploration. Run it at your own risk—Hostman offers no guarantees on its performance, security, or fit for your setup. No warranties, explicit or implied, come attached. You’re in charge of testing, deploying, and managing it. Any issues or fallout? That’s your domain to handle.