Copy Pages Between Sites in Multisite Network

Let me show you an easy way to duplicate WordPress pages between any of your sites within a multisite network.

Or to be more specific, I am about to show you two ways how you can do that:

Either way, by the end of this tutorial, we will have brand-new bulk actions on the “All Pages” admin page. However, each page can also be copied individually to a specific sub-site in case you decide to go for the plugin approach.

Copy Pages between Sites within WordPress Multisite network
As you can see, in this screenshot, it says “Move to…” instead of “Copy to…”. You can choose what suits you best, but if you want to move a page to another multisite site and remove the original one, it can be achieved with just one extra line of code.

By the way, if you’re not using a WordPress Multisite network, check out this article.

Copy or Move Pages Between Sites in a Multisite Network Programmatically

Let’s start with the programmatic approach. For your convenience, I decided to split this chapter into three parts.

1. Creating a bulk action

In this part, we’re just going to create bulk actions for every site in a multisite network. To achieve that, I am going to use the get_sites() WordPress function. By the way, don’t forget to check out my other in-depth tutorial about bulk actions in WordPress.

// add bulk actions
add_filter( 'bulk_actions-edit-page', 'rudr_my_bulk_multisite_actions' );
function rudr_my_bulk_multisite_actions( $bulk_array ) {

	$sites = get_sites( 
		array(
			// 'site__in' => array( 1, 2, 3 )
			'site__not_in' => get_current_blog_id(), // exclude the current blog
			'number' => 50,
		)
	);
	
	if( $sites ) {
		foreach( $sites as $site ) {
			$bulk_array[ "move_to_{$site->blog_id}" ] = "Move to {$site->blogname}";
		}
	}

	return $bulk_array;

}

What is actually important to keep in mind here:

  • If you’re going to use it for other WordPress post types, not for Pages, then you need either to change or duplicate the first line and provide a post type name as a part of the hook name, for example: add_filter( 'bulk_actions-edit-{POST TYPE}' ...
  • Definitely, we need to exclude the current site from the bulk actions; it can be easily done with the help of the site__not_in parameter. We don’t want to duplicate pages on the same subsite here, right?
  • You can also notice that we passed the maximum of 50 sites to get by the get_sites() function. The thing is that WordPress Multisite networks support an unlimited number of sites, so if you have 1000+ sites in your Network, you’d better think about another implementation of this step. I can give you two hints, though – maybe you do not even need all the sites. In that case, the site_in parameter will help you, or, if you need all of them, you could try to replace the default bulk action element with Select2 with AJAX search.

2. Copying every selected page to another site in a multisite network

In this part, we’re going to make our brand-new bulk action to do the thing. I mean, actually, to copy pages between sites in our multisite network.

For your convenience, I decided to create a separate PHP function rudr_copy_page_to_site():

/**
 * WordPress Multisite: Copy or Move Pages between Sites
 *
 * @author Misha Rudrastyh
 * @link https://rudrastyh.com/wordpress-multisite/copy-pages-between-sites.html
 */
function rudr_copy_page_to_site( $post_id, $blog_id ) {

	// first of all we need to get some page information from the current site
	// get the original WP_Post object as an array
	$post = get_post( $post_id, ARRAY_A );
	if( ! $post ) {
		return;
	}
	// get all the page meta
	$post_meta = get_post_custom( $post_id );
	// empty ID field, to tell WordPress to create a new post, not update an existing one
	$post[ 'ID' ] = '';

	// once we collected all the page information, we are ready to switch to another blog and create a page copy there
	switch_to_blog( $blog_id );

	// insert the page
	$inserted_post_id = wp_insert_post( $post ); // insert the post
	// add page meta
	foreach( $post_meta as $key => $values) {
		// if you do not want weird redirects
		if( '_wp_old_slug' === $key ) {
			continue;
		}
		foreach( $values as $value ) {
			add_post_meta( $inserted_post_id, $key, $value );
		}
	}

	restore_current_blog();

	// if you want just to copy pages, comment this line
	wp_delete_post( $post_id );
	
}

In this function:

  • It will work out great for regular WordPress posts or even custom post types. The only thing you need to remember is that I didn’t do anything with post taxonomies here, because by default, pages don’t have them.
  • Also, it is worth checking whether a page already exists on another site; you can use the get_page_by_path() function for that purpose, for example.
  • I definitely excluded _wp_old_slug meta key when copying page custom fields, but consider that there could be more system custom fields worth excluding, for example _edit_lock.
  • Though everything in this function seems to be working well, it is very simplified and can not work in complex cases, for example, when you have images in your ACF fields or using Elementor for pages. In that case, I recommend the plugin approach.

Also, we need to connect this PHP function to a bulk action trigger.

add_filter( 'handle_bulk_actions-edit-page', 'rudr_bulk_action_multisite_handler', 10, 3 );
function rudr_bulk_action_multisite_handler( $redirect, $doaction, $object_ids ) {

	// we need query args to display correct admin notices
	$redirect = remove_query_arg( array( 'misha_posts_moved', 'misha_blogid' ), $redirect );

 	// our actions begin with "move_to_", so let's check if it is a target action
	if( strpos( $doaction, 'move_to_' ) === 0 ) {
		$blog_id = str_replace( 'move_to_', '', $doaction ); // get blog ID from action name

		foreach ( $object_ids as $post_id ) {
			// that's our custom function
			rudr_copy_page_to_site( $post_id, $blog_id );
		}

		$redirect = add_query_arg( array(
			'misha_posts_moved' => count( $object_ids ),
			'misha_blogid' => $blog_id
		), $redirect );

	}

	return $redirect;

}

3. Admin notices

Last but not least, once pages are copied between sites, an appropriate admin notice should be displayed. We can do it with the admin_notices action hook. There is also another one, which is network_admin_notice, but we don’t need it because it is actually intended for network admin pages like “Sites”, “Users”, etc.

// show an appropriate notice 
add_action( 'admin_notices', 'rudr_bulk_multisite_notices' );
function rudr_bulk_multisite_notices() {

	if( ! empty( $_REQUEST[ 'misha_posts_moved' ] ) ) {

		// because I want to add blog names to notices
		$blog = get_blog_details( $_REQUEST[ 'misha_blogid' ] );

		// depending on ho much posts were changed, make the message different
		echo '<div class="updated notice is-dismissible"><p>';
		printf(
			_n( 
				'%d post has been moved to "%s".', 
				'%d posts have been moved to "%s".', 
				$_REQUEST[ 'misha_posts_moved' ]
			),
			$_REQUEST[ 'misha_posts_moved' ], 
			$blog->blogname 
		);
		echo '</p></div>';

	}

}

Copying Pages Between Sites with a Plugin (Recommended)

In this part, we’ll talk about my Simple Multisite Crossposting plugin.

Let’s say you tried to use the code mentioned above, but what if:

  • You don’t want to copy posts or pages between sites using only bulk action, and you want to do it when actually editing a post or page,
  • You want to copy featured images or images in custom fields (ACF, for example),
  • You want categories, tags, or custom taxonomy terms to be added to a copied version of a post,
  • You’re using Elementor or another page builder.

Well, that’s when my plugin comes in 😁 It allows you to implement everything mentioned above and much more, actually.

You can still use bulk actions:

WordPress Multisite: Copy pages between sites using bulk actions

Or you can select specific sites directly when editing a page in a classic or block editor:

WordPress Multisite - copying pages between sites using the block editor
You can select specific subsites with checkboxes.

If you have any questions, guys, either about the plugin or about the code, please let me know in the comments section below.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X