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.

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.

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.
| Hook | Description |
|---|---|
save_post | This 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:
- Using Plugin Sidebars (the correct way),
- 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:

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
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
A client asked me to auto push posts from one site to another (the canonical will be the original site). I was going to buy your plugin and then, with some time on my hands, to use the very helpful tutorial to try write a basic version.
Without revealing too much about your plugin (which I will probably buy), how do you handle categories and tags where the IDs are likely different between source and destinations? Do you send cat/tag names in the REST API body?
Hi Damien,
Yes, categories and tags have different IDs for sure. In that case, we need to send one more REST API request to map the categories from the current site with categories on the remote site (assuming they have the same slugs).