Misha Rudrastyh

Misha Rudrastyh

WordPress Developer

How to Duplicate Posts and Pages Without Plugins

It is very useful when you work with a lot of similar posts. So, when you copy a post, you don’t have to enter all metadata, taxonomies etc. each time manually.

June 17, 2013August 4, 2016 105

Duplicating posts is a very useful functionality when you work with a lot of similar posts (for example products in online shop). Especially if the posts have the same custom fields values, but the different post title and content.

It means that you do not have to re-enter custom fields, post tags and categories each time.

This is the example:

Duplicate post link in post row actions.

When you click the «Duplicate» link, the post will be cloned, but it won't be published, it will be saved as a draft and you will be redirected to the post edit admin page.

This is enough easy to do, so, look at the following code, insert it into your theme functions.php file (or into the another file if you know what to do).

 * Function creates post duplicate as a draft and redirects then to the edit post screen
function rd_duplicate_post_as_draft(){
	global $wpdb;
	if (! ( isset( $_GET['post']) || isset( $_POST['post'])  || ( isset($_REQUEST['action']) && 'rd_duplicate_post_as_draft' == $_REQUEST['action'] ) ) ) {
		wp_die('No post to duplicate has been supplied!');
	 * get the original post id
	$post_id = (isset($_GET['post']) ? absint( $_GET['post'] ) : absint( $_POST['post'] ) );
	 * and all the original post data then
	$post = get_post( $post_id );
	 * if you don't want current user to be the new post author,
	 * then change next couple of lines to this: $new_post_author = $post->post_author;
	$current_user = wp_get_current_user();
	$new_post_author = $current_user->ID;
	 * if post data exists, create the post duplicate
	if (isset( $post ) && $post != null) {
		 * new post data array
		$args = array(
			'comment_status' => $post->comment_status,
			'ping_status'    => $post->ping_status,
			'post_author'    => $new_post_author,
			'post_content'   => $post->post_content,
			'post_excerpt'   => $post->post_excerpt,
			'post_name'      => $post->post_name,
			'post_parent'    => $post->post_parent,
			'post_password'  => $post->post_password,
			'post_status'    => 'draft',
			'post_title'     => $post->post_title,
			'post_type'      => $post->post_type,
			'to_ping'        => $post->to_ping,
			'menu_order'     => $post->menu_order
		 * insert the post by wp_insert_post() function
		$new_post_id = wp_insert_post( $args );
		 * get all current post terms ad set them to the new post draft
		$taxonomies = get_object_taxonomies($post->post_type); // returns array of taxonomy names for post type, ex array("category", "post_tag");
		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);
		 * duplicate all post meta just in two SQL queries
		$post_meta_infos = $wpdb->get_results("SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id=$post_id");
		if (count($post_meta_infos)!=0) {
			$sql_query = "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) ";
			foreach ($post_meta_infos as $meta_info) {
				$meta_key = $meta_info->meta_key;
				$meta_value = addslashes($meta_info->meta_value);
				$sql_query_sel[]= "SELECT $new_post_id, '$meta_key', '$meta_value'";
			$sql_query.= implode(" UNION ALL ", $sql_query_sel);
		 * finally, redirect to the edit post screen for the new draft
		wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) );
	} else {
		wp_die('Post creation failed, could not find original post: ' . $post_id);
add_action( 'admin_action_rd_duplicate_post_as_draft', 'rd_duplicate_post_as_draft' );
 * Add the duplicate link to action list for post_row_actions
function rd_duplicate_post_link( $actions, $post ) {
	if (current_user_can('edit_posts')) {
		$actions['duplicate'] = '<a href="admin.php?action=rd_duplicate_post_as_draft&amp;post=' . $post->ID . '" title="Duplicate this item" rel="permalink">Duplicate</a>';
	return $actions;
add_filter( 'post_row_actions', 'rd_duplicate_post_link', 10, 2 );

But what if this code works only for posts, not for pages or any registered post types? Do not worry, all we need to do is to change the last filter to this:

add_filter('page_row_actions', 'rd_duplicate_post_link', 10, 2);

For custom post types view this example in comments.
You can use several filters at the same time, of course.

Only new posts about WordPress

once a week

Comments 105

  • Thanks a lot. So simple to add to functions.php and works great on WP 3.6.1.
    You saved me a lot of time. Thanks again.

  • Hi,
    thanks for this great plugin.
    Just what I was lookin’ for. Works like a charm :-)


  • Ready to add the function to my Event plugin. Going to use it for setting up repeating events. I assume I use the $arg and $taxonomies from my custom post type rather than the ones you have listed.

  • Thank you so much! amazing!I got it to work the first time.

  • Hi,

    this code works smooth and so easy to use :)

    I just have one thing.

    It I duplicate a post which includes a gallery, than the items not visible in the copy at “Create Gallery > Media Library > Uploaded to this post”. It shows “No items found. Drop files anywhere to upload”.

    Of course, it shows the items in general, just not listed in the dropdown filter “Uploaded to this post”.

    It’s possible to “fix” it?

    Jingz :)

  • Leo CavalliniDecember 8, 2013 at 02:12

    Hey pal, thanks a lot… I was ready to install a plugin but am one of those who prefer the less amount of plugins possible.

    I’m gonna repeat the jingz question. Aren’t images related to the posts by slugs into the db? Can’t I relate the same image files to the duplicate post with different slugs? I mean… WP will automatically create new slugs when I try to insert the same values to the table, no?

    How do the featured image duplication works? Isn’t it what we are expecting?

    I’m using it in a bilingual blog that shares all the same stuff, custom fields, tags and category. Only title, slug and content is different from one sibling post to another. So doing this image linkage automatically would be beautiful.

    One more question, do I get the relation between both posts on-the-fly, in a way I can print the duplicated post and tell who was the original one?

    Congrats for your work, it saved me immensely.

    • Hi,
      This plugin copy the featured thumbnails but don’t save the image relations to the post.
      It don’t duplicate the images as well.
      Yes, you can save the original post ID as new post custom field value.

  • This is a great function. I was wondering is it possible to remove the default category. Everytime I do a duplicate, it populates the default category too.

  • Hi

    Misha, the post images are not duplicating when i use your code.Can you give a solution for duplicating the post gallery

    • Hi!
      do you mean images, attached to post? Then you should also include the loop with duplicating all post children (post type attachment).

      • Am I missing something? Where is the loop you speak of that I need to include so that I may copy the image attached to the post?

  • This works only for posts and not for pages even if we change the last line of the above code.

    Please check or let us know the exact code to duplicate pages.

  • Dennis OuelletteFebruary 18, 2014 at 14:02

    Worked the first time. Awesome function. Thanks!

  • Brilliant! Many many thanks for sharing your superb code, Misha (including your very helpful comments). It does exactly what I wanted and will save lots of time in creating pages.

  • Kevin BrandsMarch 11, 2014 at 12:03

    Hello Misha,

    I’ve succesfully used your code on a custom post type I’ve created. But suddenly, it stopped working. I’ve been thinking why it stopped working, and I don’t have a clue.

    I’ve tried disabling/deleting plugins. No luck.

    Then I tried enabling/disabling it on other custom post types I’ve created in the same theme. The strange thing is: when I enable it for the standard ‘post_row_actions’, it shows on one of the custom post types. But when I enable it specifically for this custom post type like so ‘vragen_row_actions’, it doesn’t work.

    As for the other custom post types: it doesn’t work anymore at all.

    Do you know what could be going on?

  • Misha

    Great snippet of code! Thanks so much. I’m having the same problem as Kevin above… using ‘event_row_actions’ doesn’t work, but if I use the generic ‘post_row_actions’ it was, albeit for all posts, which is what I’m trying to avoid.

    That said, I can get around that by check post type in the function itself by add ‘($post->post_type == ‘event’)’ to the if statement.


    My second question is about the redirect… Since I’m using this for an events plugin, I’d rather not redirect the user to the post edit screen. I would prefer that the duplicate post is created, saved as a draft, and the user remains on the list view page. They could then click quick edit to simply change the date of the event since thats all that changes.

    I tried just commenting out the wp_redirect() line but this still tried to redirect the user and produces a blank screen.

    Any thoughts on how to accomplish this?

    Any help is greatly appreciated.

    • MishaAuthorMarch 13, 2014 at 08:03

      Hi Bruce,
      the answer to you first question is here,
      as for redirect — just change this line of the code

      wp_redirect( admin_url( 'post.php?action=edit&post=' . $new_post_id ) );


      wp_redirect( admin_url( 'edit.php' ) ); // or edit.php?post_type={here your custom post type name}
  • tslil aloniMarch 16, 2014 at 12:03


    first of all, thanks!

    second – can you write the filter for duplicating portfolio items?

    thanks alot!!!

  • SWEET bit of code! – Saved me a bunch of time, a bunch of times!

    Cheers x

  • Misha, this is such a great solution. Saves me tons of time when I’m creating dummy content and it already includes taxonomy and meta data. It’s perfect! This will be a huge time saver for admins/clients too! Thank you for sharing this!

  • Thanks for a simple, effective method to duplicate posts! most appreciated, worked like a charm.

  • If I have portfolio content type, how would I use this code to do something similar. Is there a way to modify this easily?

  • Paal JoachimJanuary 16, 2015 at 15:01

    I added your duplicate post code to my web page:

    Have a great day!

    Thank you for the snippet!

    Have you thought about creating a WordPress trac ticket https://core.trac.wordpress.org/ suggesting that duplicate post is added to Core?

  • Fab! Works perfectly! :)

    Thanks so much!!

  • I hate to be the bearer of bad news, but this is WIDE open to hostile SQL injection and you fail to sanitize any of the GET/REQUEST variables with absint() and such like you should. I could drop your tables/database in a couple of characters on any site that uses this.

    Outside of that it looks rather helpful so thank you for posting :)

  • For some reason, the meta data never would copy for me, so I replaced your “duplicate all post meta” section with this, and it seemed to work like a charm:

    $data = get_post_custom($post_id);
    foreach ( $data as $key => $values) {
    	foreach ($values as $value) {
    		add_post_meta( $new_post_id, $key, $value );
  • Hi.. I try this code in my local host.. and try to write with duplicate.. but the Link was numbered and still publish..

    any ideas for it or it’s not work in local host ?

  • Is there a way to just duplicate the featured image? Instead of copying all meta information, the only meta I want is the featured image. How would I go about that?


    • MishaAuthorAugust 14, 2015 at 05:08

      I think it will be enough to copy the meta with _thumbnail_id key.

      • So how would I rewrite the following code to just duplicate the


        key? Sorry I’m not really a database guy so I’m not too familiar with how to handle queries.

         * duplicate all post meta
        $post_meta_infos = $wpdb->get_results("SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id=$post_id");
        if (count($post_meta_infos)!=0) {
          $sql_query = "INSERT INTO $wpdb->postmeta (post_id, meta_key, meta_value) ";
          foreach ($post_meta_infos as $meta_info) {
            $meta_key = $meta_info->meta_key;
            $meta_value = addslashes($meta_info->meta_value);
            $sql_query_sel[]= "SELECT $new_post_id, '$meta_key', '$meta_value'";
          $sql_query.= implode(" UNION ALL ", $sql_query_sel);


  • I have two custom post types, “Books” and “Author”. I want to get the plugin to duplicate from one post type to the other. So in “Books” I have a post called “Moby Dick” instead of “Duplicate Post” I have “Author details”, when I click “Author details”, it will duplicate the “Moby Dick” post to my other post type “Author”. Under the “Author” post type I will have a new post called “Moby Dick – Author”.

    Is that possible? Thanks in advance.

  • The code duplicates posts and custom post types well but I am unable get an metapostdata to copy across?

    Thanks for your help

    • Hi,
      please, try this.

      • Thanks Misha

        I tried Corbin’s code and it worked on a standard post – I added an excerpt and that was duplicated.

        I need a bit more help if you guys have time. The theme I am using has what seems like the post type “Event” and this has extra meta fields, when I look at it in the database the post_type field is “event” – however, the duplicate link appears when this code is added:

        add_filter( 'post_row_actions', 'rd_duplicate_post_link', 10, 2 );

        and disappears if I change this to:

        add_filter( 'event_row_actions', 'rd_duplicate_post_link', 10, 2 );

        So I am not convinced that Event is it’s own custome post type? I’m getting confused.

        Thanks for your expertise.

  • p.s. I tried both

    add_filter( 'event_row_actions', 'rd_duplicate_post_link', 9999, 2 );


    add_filter( 'post_row_actions', 'rd_duplicate_post_link', 9999, 2 );
    • There are only two filters:

      • page_row_actions — for page type,
      • post_row_actions — for posts and another custom post types.

      Ok, so I recommend you to make the following changes:

      function rd_duplicate_post_link( $actions, $post ) {
      	if ($post->post_type=='event' && current_user_can('edit_posts')) {
      		$actions['duplicate'] = '<a href="admin.php?action=rd_duplicate_post_as_draft&amp;post=' . $post->ID . '" title="Duplicate this item" rel="permalink">Duplicate</a>';
      	return $actions;
      add_filter( 'post_row_actions', 'rd_duplicate_post_link', 10, 2 );
      • Hi Misha
        I tried this function for my ‘Events’ but is not working to me, is not even getting inside the function, I added a print_r(Hello) but is only working on the posts listing?

        function rd_duplicate_post_link( $actions, $post ) {
          print_r("Hello world!");
        	if ($post->post_type=='events' && current_user_can('edit_posts')) {
        		$actions['duplicate'] = '<a href="admin.php?action=rd_duplicate_post_as_draft&amp;post=' . $post->ID . '" title="Duplicate this item" rel="permalink">Duplicate</a>';
        	return $actions;
        add_filter( 'post_row_actions', 'rd_duplicate_post_link', 10, 2 );

        any help would it be appreciated.
        Thank you!

        • MishaAuthorMay 21, 2016 at 07:05

          Hi Marcos,
          are you sure, that your post type name is “events” not “event”? Usually plural forms are not used for post type names.

  • Hello,

    This works great, however what if I am using a polylang plugin? I have a multilanguage site and when clicking duplicate it only duplicates the current language and not both languages. Any idea how to duplicate both language posts at the same time. They are somehow linked to each other but when clicking duplicate the post is duplicated however the link stays the same pointing the old post translation.


  • Hi MIsha, I setup a child theme, and also have a functions.php file in the child folder.

    When I paste the code into the child folder functions.php, it does NOT work; when I paste it in the parent functions.php, it does work.

    Is there a way to make it work in the child folder that I’m missing? Thanks!

    • MishaAuthorJanuary 9, 2016 at 08:01

      Hi Troy,

      I made some tests — the code works good with the child themes. I’m not sure where your problem is, please try to change filter priority here.

      add_filter( 'post_row_actions', 'rd_duplicate_post_link', 9999, 2 ); // from 10 to 9999 for example
      • Genius Misha – that change from 10 to 9999 worked a treat for my child theme! Thanks for the time and effort. Great piece of code generously given :-)

  • Captain BaconFebruary 4, 2016 at 13:02

    Thank you Misha, You saved my life!

  • Thanks :)

  • Firstly, great code. It works out of the box.

    What if I want a duplicate link/button on the page/post itself? Is that possible?

    • MishaAuthorJune 9, 2016 at 09:06

      do you mean post/page edit wp-admin/post.php?post=2202&action=edit pages?

      • Hi Misha!

        First, thank you! This is saving me a ton of time and is one less plugin to install.

        Second, I think I am trying to do something similar to JJ Human’s question. I have built a site where all of the editing of posts is done through ACF forms on the front end. I am limiting access to the wp-admin pages to a few users.

        I already changed wp_redirect() to redirect to the permalink of the new post. This isn’t working. Right now it redirects to a blank version of the original post.

        Is there anyway to execute this code to redirect to the front end version of the website?

        Thanks so much!


        • UPDATE: I figured out how to publish the post and redirect it from the admin link. Turns out I was using an invalid post status.

          Now I just need to create a template tag that will create a link to do the same thing on my single.php page.

  • Thanks! I refuse to install any more plugins for these small things:)
    Works perfectly:)

  • Hi. I just have tried it and it looks great.
    I simply want to thank you, Misha and other contributors, for sharing the code and especially for the willingness to help and collaborate.

    I also have few questions, possibly naive since I am not a deep technical guy in php and wordpress:

    1. If I append your code in my php (I have other code already there) it won’t execute. If I prepend it (as the first block of code in the file) then it works fine. I assume there would be no restriction on where I place the code inside the file, right?
    (I ask that because I then suspect I have some errors in my pre-existing code :-|

    2. Though I could not find it explicitly stated, I assume I can have both filter working at the same time:

    // both filter working
    add_filter( 'post_row_actions', 'rd_duplicate_post_link', 10, 2 );
    add_filter('page_row_actions', 'rd_duplicate_post_link', 10, 2);

    It seems to work for me, so that I can have the “duplicate” option both on posts and pages at the same time.
    Or was it intended to have just one filter active at a time?
    Thanks a lot!

    • MishaAuthorJuly 27, 2016 at 05:07

      Hi Cristiano,
      yes, there are no restrictions of where you placing the code, maybe you place it incorrectly after the closing ?> tag.

      Yes, you can use both filters at the same time :)

  • Hi! I just added this to my blog and it works fantastic!
    One question though, the page views for the posts are also duplicated so it didn’t restart at 0 (and I won’t be able to get an estimate of how many people have viewed my new post).
    Is there a way to reset the page view of the new duplicated post to 0?
    Thank you for making my life easier :)

  • Excellent!

  • I’ve been using a post duplicating plugin for a while, and I think it has royally screwed my SEO. The duplicator also duplicates the meta data, so it won’t let me push the cloned posts out (as a new post) to social media. Is this also something I can control as a code level, as easily as the above?
    Thanks for your thoughts.

    • MishaAuthorAugust 15, 2016 at 10:08

      Hi Kelley,
      I’m not sure that I understand your question.

      So, do you want to prevent the duplication of the meta data?

      • Sorry for not being clearer, Misha. When I use a post duplicating plugin, I can’t automatically push the cloned post out to social media via Jetpack. Jetpack reads the meta data of the original post; therefore, it won’t recognize the cloned post as an original, to be pushed out to social media upon publishing. If I duplicate a post by using this code modification, will I be able to push cloned posts out as new ones using Jetpack (or another publicizing plugin)?
        Thank you!

        • MishaAuthorAugust 16, 2016 at 09:08

          Ok, now I understand your problem.

          Some thoughts about it:

          1. Duplicating posts won’t increase your SEO, it may bring you the opposite effect I think.

          2. On the official Jetpack website is said that Publicize never will be triggered for the cloned posts — I don’t know how it checks it — you can try to clone post without meta data for a first time and look at the effect. To turn off copying meta data, remove from the code lines 67 – 77. If after that the post will be pushed to social media, then everything is left to do for you – just to find out what meta data keys to clone.

  • Thanks this is just what I needed

  • Very nice, huge help. Thank you!

  • doesnt work in WP 4.6

  • Awesome!
    Thank you so much.

  • Thanks for this nice script.

    you should use a prepare state for the SQL statment, but adding “(int)” to this should make it a bit saver to:

    $post_id = (int)(isset($_GET['post']) ? $_GET['post'] : $_POST['post']);

    Just to make sure the page is not locked when someone is using it when you make a copy:

    if ($meta_key != '_edit_lock') $sql_query_sel[]= "SELECT $new_post_id, '$meta_key', '$meta_value'";
  • Hi. I’m trying to modify this to allow a subscriber to republish a post on the front end (for an image and video social site so we don’t care as much about duplicate content). These are the changes I made but it’s not creating the post. Thanks for any insight you may have.

    'post_status'    => 'publish'

    added bottom two lines here:

    /* Get original post author and id info */
    $orig_author = get_post_meta($post_id, 'orig_author', true);
    $orig_post_id = get_post_meta($post_id, 'orig_post_id', true);
    if ( $orig_author == '') {
          $orig_author = $post->post_author;
           $orig_post_id = $post->ID;
     * duplicate all post meta just in two SQL queries
    $post_meta_infos = $wpdb->get_results("SELECT meta_key, meta_value FROM $wpdb->postmeta WHERE post_id=$post_id");
    $post_meta_infos['orig_author'] = $orig_author;
    $post_meta_infos['orig_post_id'] = $orig_post_id;

    and called it using this function instead of post_row_actions hook…

    function man_renail_button( $post_id ) {
    	if(current_user_can('copy_posts')) {
    		$link = '<a class="simplebtn red" href="admin.php?action=rd_duplicate_post_as_draft&amp;post=' . $post_id . '" title="Renail" rel="permalink">><i class="fa fa-gavel"></i> Renail This</a>';
    		echo $link;

    Then redirected to post with

    wp_redirect( get_permalink( $new_post_id ) );

    It did not create the post however and went to a 404 page. Tested with Admin role to ensure roles were not an issue but still didn’t work. Any ideas or something I’m missing?

    • Hi Tim,
      Does it create a draft or it it doesn’t create anything?

    • It doesn’t create anything.

      • First of all, if you insert code like this in website front-end, it won’t provide access to admin.php

        $link = '<a class="simplebtn red" href="admin.php?action=rd_duplicate_post_as_draft&amp;post=' . $post_id . '" title="Renail" rel="permalink">><i class="fa fa-gavel"></i> Renail This</a>';

        Try to change it to:

        $link = '<a class="simplebtn red" href="' . site_url() . '/wp-admin/admin.php?action=rd_duplicate_post_as_draft&amp;post=' . $post_id . '" title="Renail" rel="permalink">><i class="fa fa-gavel"></i> Renail This</a>';
  • Thank you so much for this post. Super helpful.

  • Oren Zur-ShavitSeptember 29, 2016 at 14:09

    So helpful, TNX!

  • Absolutely wonderful, simple and complete solution. Thank you for sharing.

  • Hello,

    What a nice script ;) I have successfully integrated it with every improvement of this comment thread. But I really want to expand it to fit my needs, and I require some help in order to achieve this.

    What I want to do is to create 2 new “duplicate link” in order to have a “Duplicate” link, a “Generate the news related article” link and finally a “Generate the forum article” link.

    Why have I these needs? Cause for each subject of my website, I have 2 related articles with the same meta, taxonomies etc… only the title and the content will change. If I publish a post named “Photoshop”, I’ll create just after this 2 others related posts “News – Photoshop” and “Forum – Photoshop”.

    My question now : how can I achieve this with a single function? I can actually make 3 different functions for the 3 “duplicate link” I want, but this will makes unnecessary request.

    These 2 additional duplicate links must change the title by adding a prefix (‘News -‘ and ‘Forum -‘), and erasing the content (kinda easy, just delete the line from the $args).

    If someone can help me to achieve this, I’ll really appreciate this.

  • Great post and still valid over three years later :) Thank you.

    Just a heads up if anyone else comes along here and have an issue where if you duplicate a post, edits it but keeps it in draft mode and then checks the original post in the frontend only to see that the content of the original post seems to have been updated as well.

    This seems has to do with the fact that


    of the new post gets set to the same value as the original. I solved this by simply removing the line

    'post_name'  => $post->post_name,

    (40 in the example code at the time of writing this). This sets the post-name to a “slug version” of the title (which, even though it is set to the same as the original post, will be changed by WP when wp_insert_post is executed).

  • Misha-san, thanks so much! I just tried and worked like a champ. Saved my time!!!!!

Leave comment

Please, enter a comment
Please, enter a name
Incorrect email
© 2016 Misha Rudrastyh
Developed on with