How to Duplicate WordPress Posts, Pages, & CPTs (Code Snippet + Plugins)

Table of Contents

Want to get professional advice?

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Table of Contents

Duplicating WordPress posts, pages, or custom post types (CPTs) is a workflow lifesaver. Whether you’re creating similar products, new service pages, or simply want a draft to experiment with, a quick duplicate saves immense time.

While many plugins offer this functionality, here’s how to do it with a simple code snippet, no extra plugins required.

Why Duplicate Posts?

  • Save time: Replicate all content, custom fields, categories, and tags instantly.
  • A/B testing: Create a duplicate to test design changes or content variations.
  • Templates: Use a well-structured post as a template for new content.
  • Workflow: Quickly generate drafts for new products, services, or events.

The Code Snippet (functions.php)

Add this code to your theme’s functions.php file or a custom plugin. We recommend using a child theme for functions.php modifications so your changes aren’t lost during theme updates.

/**
 * Digitizer: Duplicate Post Functionality
 * https://www.digitizer.studio/post-duplicator
 */

// Add the duplicate link to action list for post_row_actions
// for โ€œpostโ€œ and custom post types
add_filter( 'post_row_actions', 'dz_duplicate_post_link', 10, 2 ),
// for โ€œpageโ€œ post type
add_filter( 'page_row_actions', 'dz_duplicate_post_link', 10, 2 ),

function dz_duplicate_post_link( $actions, $post ) {
    if ( ! current_user_can( 'edit_posts' ) ) {
        return $actions,
    }
    $url = wp_nonce_url(
        add_query_arg(
            array(
                'action' => 'dz_duplicate_post_as_draft',
                'post'   => $post->,ID,
            ),
            'admin.php'
        ),
        basename( __FILE__ ),
        'duplicate_nonce'
    ),
    $actions[ 'duplicate' ] = '<,a href=โ€œ' . $url . 'โ€œ title=โ€œDuplicate this itemโ€œ rel=โ€œpermalinkโ€œ>,Duplicate<,/a>',
    return $actions,
}

/*
 * Function creates post duplicate as a draft and redirects then to the edit post screen
 */
add_action( 'admin_action_dz_duplicate_post_as_draft', 'dz_duplicate_post_as_draft' ),
function dz_duplicate_post_as_draft(){

    // check if post ID has been provided and action
    if ( empty( $_GET[ 'post' ] ) ) {
        wp_die( 'No post to duplicate has been provided!' ),
    }

    // Nonce verification
    if ( ! isset( $_GET[ 'duplicate_nonce' ] ) || ! wp_verify_nonce( $_GET[ 'duplicate_nonce' ], basename( __FILE__ ) ) ) {
        return,
    }

    // Get the original post
    $post_id = ( isset( $_GET[ 'post' ] ) ? absint( $_GET[ 'post' ] ) : absint( $_POST[ 'post' ] ) ),
    $post = get_post( $post_id ),

    // If post not found, die
    if ( ! $post || ! current_user_can( 'edit_post', $post_id ) ) {
        wp_die( 'You don\'t have permission to duplicate this post.' ),
    }

    // Create the duplicate post array
    $args = array(
        'comment_status' =>, $post->,comment_status,
        'ping_status'    =>, $post->,ping_status,
        'post_author'    =>, get_current_user_id(),
        'post_content'   =>, $post->,post_content,
        'post_excerpt'   =>, $post->,post_excerpt,
        'post_name'      =>, $post->,post_name . '-copy',
        'post_parent'    =>, $post->,post_parent,
        'post_password'  =>, $post->,post_password,
        'post_status'    =>, 'draft', // Duplicate as draft
        'post_title'     =>, $post->,post_title . ' (Copy)',
        'post_type'      =>, $post->,post_type,
        'to_ping'        =>, $post->,to_ping,
        'menu_order'     =>, $post->,menu_order,
    ),

    // Insert the post into the database
    $new_post_id = wp_insert_post( $args ),

    // Copy post meta
    $post_meta_keys = get_post_custom_keys( $post_id ),
    if ( ! empty( $post_meta_keys ) ) {
        foreach ( $post_meta_keys as $meta_key ) {
            $meta_values = get_post_custom( $post_id, $meta_key ),
            foreach ( $meta_values as $meta_value ) {
                add_post_meta( $new_post_id, $meta_key, maybe_unserialize( $meta_value ) ),
            }
        }
    }

    // Copy taxonomies (categories, tags, custom 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 ),
    }

    // Redirect to the new draft's edit page
    wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) ),
    exit,
}

How It Works

This code does two things:

  1. Adds a “Duplicate” link to the post/page actions list in the WordPress admin.
  2. Creates a draft copy of the post/page, including its content, custom fields, categories, and tags.

When you click “Duplicate,” a new draft is created and you’re redirected to its edit screen. The new post’s title will have “(Copy)” appended, and its slug will have “-copy” appended.

When to Use This Snippet

  • You need to create multiple similar landing pages.
  • You’re building a large e-commerce store with many similar product pages.
  • You want to experiment with changes to a live post without affecting it.
  • You manage a site with custom post types and need to quickly replicate content.

Alternatives: Plugins

If you prefer a plugin for this functionality, popular options include:

  • Duplicate Post (by Enrico Battocchi), most popular, highly rated.
  • Yoast Duplicate Post, a lightweight fork of the original.

Plugins often offer more features like custom post statuses for duplicates, different duplication methods, and role permissions. If the snippet isn’t enough, a plugin is the next step.

Need custom WordPress development or workflow optimizations? Get in touch, we build custom solutions that save you time.

Yes, if done correctly and in a child theme. Always back up your site before adding custom code. Alternatively, use a custom plugin for code snippets to avoid losing changes during theme updates.
Yes. The code copies all post meta (custom fields) from the original post to the new draft. This includes fields from ACF, JetEngine, or any other custom field solution.

About the author

Ben Kalsky, Founder & Partner at Digitizer

Ben has 15+ years of experience building websites for technology companies, e-commerce businesses, and service providers across Israel and internationally. As co-founder of Digitizer, he’s delivered over 100 projects ranging from โ‚ช5,000 landing pages to โ‚ช100,000+ enterprise platforms.

Notable work includes:

  • Building platforms for companies later acquired by Fortune 500 firms (CrowdStrike, Nvidia)
  • Migrating 50+ businesses from proprietary platforms to WordPress, saving an average of โ‚ช80,000/year in platform fees
  • Managing infrastructure for 100+ websites with 99.9% uptime over 3 years

Ben specializes in WordPress, WooCommerce, automation, and helping businesses make smart technology decisions that scale. His approach: practical, process-based solutions that drive measurable business growth – no buzzwords, no vendor lock-in.

On Digitizer’s blog, he shares real-world insights on website pricing, platform selection, and avoiding costly mistakes when building digital infrastructure.

Share the article

Copy

More articles