How to Auto-Post From One WordPress Site to Another

In this tutorial, I will show you how to automatically post an article to another WordPress website with the help of a couple of WordPress action hooks and the REST API. But there is not only a programmatic approach; you can also do that with my Simple WP Crossposting plugin, which we will cover in this tutorial in just a little bit.

By the way, I also noticed a weird thing – in similar tutorials on this topic, you can often find recommendations to use an RSS feed of a website and a plugin that allows you to import and export the content from that feed. I would recommend that you avoid methods that include RSS feeds, as it is quite an obsolete way of copying content, because it doesn’t allow you to copy important post data like custom fields, featured images, etc.

I am not sure whether you prefer a programmatic approach or you want to use the plugin, so let me start with the plugin approach first, and then we will dive into the code.

Method 1. Auto-Post to the Other Site using a Plugin

In this chapter, I am going to cover how you can configure auto-posting to other WordPress sites with the help of my Simple WP Crossposting plugin. First of all, please install and activate the plugin on the site where you would like to do the auto-posting from, and then let’s navigate straight into the plugin settings.

1.1 Connect other sites in the plugin settings

The first thing you need to do is to open the “Sites” tab and add the other WordPress sites there.

how to add sites in plugin settings
In order to add a site, provide its URL, username, and application password. Please note that the application password is not a user profile password

1.2 Activate the “Auto Mode” option

Then, on the “General” tab, please check the “Auto mode” checkbox – it actually allows auto-posting.

After you have checked that checkbox, when you publish or update a post (page, or a custom post type) on your website, it will be automatically published or updated on all other sites you connected in the “Sites” tab.

Turn on the auto-posting to the other WordPress sites
On this settings page, you can also decide which post types and post statuses are allowed for auto-posting.

That’s pretty much it!

The plugin also includes many other optional settings that you may find useful.

More info about the plugin is on the Simple WP Crossposting plugin’s page.

Method 2. Auto-Post to the Other Site using the REST API

I guess it is time to talk about the programmatic approach. In theory, you can use the code below to create your mini-plugin, but I may assume it will only work great for super-simple posts that consist of title, content, and excerpt (or something like that).

Which hooks to use?

So, our goal here is to automatically run a function that allows us to auto-post to the other WordPress site every time we create a post (or a page, or a custom post type – we will decide it later).

Most likely, you already know about the save_post hook, but there are also save_post_{post type} and rest_after_insert_{post type}. Which one to use? Check the table below.

HookDescription
save_postThis hook runs every time a post (of any post type) is saved or updated.
save_post_{post type}This hook appeared in WordPress 3.7, and it is absolutely identical to save_post except for one thing – you can provide a post type name as a part of the hook. For example, if we’re going to run it for posts only, it will be save_post_post, for pages – save_post_page.
rest_after_insert_{post type}This one appeared in WordPress 5.0, and it is about to run only when a post is created or updated via the REST API. So, if you’re using the Classic Editor plugin or your custom post type simply doesn’t support the Block Editor, then this hook is not for you.

In my opinion, it would be great to use the save_post_{post type} hook, because there will be no need to check for a post type inside the function. Also, I think we can do the whole thing for standard WordPress posts; in that case, our hook will be save_post_post.

add_action( 'save_post_post', 'rudr_auto_post_to_other_site', 25, 2 );

function rudr_auto_post_to_other_site( $post_id, $post ) {
	
	// do the auto-post to the other WordPress site here
	
}

The thing with obsolete meta boxes in the Block Editor

Today, there are two ways of presenting custom fields in Gutenberg:

  1. Using Plugin Sidebars (the correct way),
  2. Using meta boxes the same way we used them before (backward compatibility).

When you choose the second way, the meta fields are going to be displayed like this:

Advanced Custom Fields example in Gutenberg
The old-fashioned way of creating custom fields.

Let’s assume that we don’t care much about how they look, but what we really care about is how save_post and rest_after_insert_{post type} are going to work.

I just want you to keep in mind that when you’re displaying meta boxes that way, save_post (and save_post_{post type} accordingly) are going to run twice – before the custom fields are saved and after the custom fields are saved. In other words, Gutenberg performs an additional request to post.php (yes, it is not a REST API request) to save the meta boxes.

How do we differentiate these kinds of requests in the code? Easier than you think.

if( isset( $_REQUEST[ 'meta-box-loader' ] ) ) && $_REQUEST[ 'meta-box-loader' ] ) {
	// saving meta boxes	
}

So, if you’re going to use meta boxes the old-fashioned way, keep it in mind.

Auto-publishing posts to the other site with the REST API

Now we know the basics and are ready to do some auto-posting. In this example, we are going to auto-post only once when the post is initially published. It means that further post updates are not going to be synced with the other site.

If you need post updates to be synced as well, or if you just got overwhelmed with the coding, please scroll back to the plugin solution.

/*
 * Auto-posting to another site
 * @author Misha Rudrastyh
 */
add_action( 'save_post_post', 'rudr_auto_post_to_other_site', 25, 2 );

function rudr_auto_post_to_other_site( $post_id, $post ) {
	
	if( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
		return;
	}
	
	if( 'publish' !== $post->post_status ) {
		return;
	}
	
	// here we need to provide username and application password
	$login = '';
	$pwd = '';
	
	// let's provide the post data for the REST API request
	$post_data = array(
		'title' => $post->post_title,
		'status' => $post->post_status,
		'date' => $post->post_date,
		'slug' => $post->post_name,
		'menu_order' => $post->menu_order,
		'content' => $post->post_content,
		'meta' => array(),
	);
	
	// so we are using some custom fields, we need to add them to the request
	$post_meta = get_post_meta( $post_id );
	foreach( $post_meta as $meta_key => $meta_values ) {
		// simplified version for this tutorial
		$meta_value = reset( $meta_values );
		// add to the REST API request body
		$post_data[ 'meta' ][ $meta_key ] = $meta_value;
	}
	
	// auto-posting to "https://ANOTHER-WEBSITE"
	wp_remote_post(
		'https://ANOTHER-WEBSITE/wp-json/wp/v2/posts',
		array(
			'headers' => array(
				'Authorization' => 'Basic ' . base64_encode( "$login:$pwd" ),
				'Content-Type' => 'application/json; charset=utf-8',
			),
			'body' => json_encode( $post_data )
		)
	);
	
}

Please keep in mind that a REST API endpoint will be different for pages and custom post types, so you will need to change /v2/posts to /v2/pages or /v2/{rest api base}.

How to check whether we’re publishing or updating a post?

If you don’t perform that kind of check, be ready for post duplicates to be created on another site. And there are different ways to check that.

First of all, you can try to compare the post publish and last modified dates like this:

if( $post->post_date !== $post->post_modified ) {
	return;
}

When I test it on my localhost, everything works perfectly, but I assume this method may not work out on some slow sites (maybe).

So the safest way is to send an extra REST API request and to check whether the post with the same slug already exists:

if( 0 === strpos( wp_get_referer(), 'post-new.php' ) {
	return;
}

$request = wp_remote_get( add_query_arg( 
	array( 
		'slug' => $post->post_name 
	), 
	'https://ANOTHER-WEBSITE/wp-json/wp/v2/posts'
) );
if( 'OK' === wp_remote_retrieve_response_message( $request ) ) {
	$posts = json_decode( wp_remote_retrieve_body( $request ) );
	if( $posts ) {
		return;	
	}
}

If you have any questions about my plugin or about the code, feel free to ask in the comments.

Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 15 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